Repository: mapbox/mr-ui
Pull request #36 (2018-08-11 01:21:50)

Docs improvements
- Made a nicer syntax highlighting scheme.
- Fixed path names to components in examples.
- Fixed some links and small details in docs.
- Updated the Assembly version.
- Added a `<meta name="robots" content="noindex" />` tag, since once we publish this page on gh-pages we don't really want this page getting indexed — at least not yet, since many examples in the docs are still very sloppy.

@danswick for review, please. I'm thinking that after this merges we could cut a release with all the pending changes.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f5cff8..86ac4e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ## HEAD
 
+- 🚨 [breaking change] Remove **IconButton** component.
 - [feature] Add **Heading** component.
 - [feature] Add **Button** component.
 - [feature] Add **IconText** component.
@@ -9,23 +10,40 @@
 - **Popover**
   - 🚨 [breaking change] Remove `themePopover`, `backgroundColor`, and `accessibleTitle` props.
   - 🚨 [breaking change] Rename `contentElementAttributes` prop to `passthroughProps`, to match incoming convention.
-  - [feature] Add `coloring` and `padded`.
+  - [feature] Add `coloring` and `padding`.
   - [chore] Update `focus-trap` and `tabbable` dependencies.
 - **PopoverTrigger**
-  - 🚨 [breaking change] Rename `triggerProps` to `passthroughTriggerProps`.
+  - 🚨 [breaking change] Rename `triggerProps` prop to `passthroughTriggerProps`.
+  - 🚨 [breaking change] Replace `display` prop with `block`.
   - [fix] If the trigger responds to focus but not click, and you focus the trigger *first* and *then* click, that click *closes* the popover instead of leaving it open even after you move the mouse away.
+  - [fix] If you use `disable` to disable a trigger with an open popover, the popover will hide.
 - **CopyButton**
+  - 🚨 [breaking change] Arbitrary props will not be passed through to the button.
+  - 🚨 [breaking change] Remove `iconButtonProps` and `feedbackTime` props.
+  - [feature] Add `block`, `className`, and `passthroughProps` props.
+  - [feature] Add `CopyButton.copyIsSupported()` static function.
+  - [feature] Improve appearance of copy instructions popover.
   - [fix] Works in modals.
+- **Copiable**
+  - 🚨 [breaking change] Remove `themeWrapper`, `themeCopyButtonContainer`, `themeCopyButton`, `themeText`, and `themeTooltip` props.
+  - [feature] Add `truncate` prop.
 - **Tooltip**
   - 🚨 [breaking change] Remove `backgroundColor` and `themeTooltip` props.
-  - [feature] Add `coloring`, `padded`, and `maxWidth` props.
+  - 🚨 [breaking change] Replace `display` prop with `block`.
+  - [feature] Add `coloring`, `padding`, and `maxWidth` props.
   - [feature] Children of Tooltip no longer need to be functions! If you provide a standard DOM element (e.g. `<button>`, `<div>`, `<span>`) or a Button component, things will just work. You can still use a function if your trigger is a custom component.
 - **Modal**
   - 🚨 [breaking change] Remove `underlayStyle`, `underlayProps`, `themeUnderlay`, `themeModal`, and `themeButtonClose` props.
   - 🚨 [breaking change] Rename `title` to `accessibleTitle`.
-  - [feature] Add `padded`, `alert`, `primaryAction`, `secondaryAction`, and `tertiaryAction` props.
+  - [feature] Add `padding`, `alert`, `primaryAction`, `secondaryAction`, and `tertiaryAction` props.
   - [fix] Modals taller than the viewport will now show padding above them: the browser will not longer automatically scroll down to the top of the modal.
   - [fix] Update `react-aria-modal` to get some bug fixes and focus-management improvements.
+- **Icon**
+  - 🚨 [breaking change] Remove `themeIcon` prop.
+  - 🚨 [breaking change] Arbitrary props will not be passed through to the `<svg>`. Use `passthroughProps`.
+  - [feature] Add `passthroughProps` prop.
+  - [feature] Add `size` prop.
+  - [fix] If `inline: true`, the corresponding style adjustment will be performed after updates as well as mounting.
 
 ## 0.2.1
 
diff --git a/README.md b/README.md
index 036433e..e58bae9 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ UI components for Mapbox projects.
 npm install @mapbox/mr-ui
 ```
 
-On Mapbox projects, pair these components with version 0.26.0 of Mapbox's custom [Assembly](https://www.mapbox.com/assembly/) build. (This is not in `peerDependencies` because you might use `<link>` and `<script>` tags instead of the npm package.)
+On Mapbox projects, pair these components with version 0.26.0+ of Mapbox's custom [Assembly](https://www.mapbox.com/assembly/) build. (This is not in `peerDependencies` because you might use `<link>` and `<script>` tags instead of the npm package.)
 
 The public Assembly build should work fine, with maybe one or two hiccups.
 
diff --git a/batfish-docs.config.js b/batfish-docs.config.js
index 258e79f..c167563 100644
--- a/batfish-docs.config.js
+++ b/batfish-docs.config.js
@@ -5,7 +5,7 @@ const path = require('path');
 module.exports = () => {
   return {
     siteBasePath: 'mr-ui',
-    stylesheets: [require.resolve('prismjs/themes/prism.css')],
+    stylesheets: [path.join(__dirname, './src/docs/prism-theme.css')],
     spa: true,
     staticHtmlInlineDeferCss: false,
     pagesDirectory: path.join(__dirname, './src/docs/pages')
diff --git a/package.json b/package.json
index 8dae9ec..6ce1f2f 100644
--- a/package.json
+++ b/package.json
@@ -79,6 +79,7 @@
     "enzyme": "^3.0.0",
     "enzyme-adapter-react-16": "^1.0.0",
     "enzyme-to-json": "^3.3.4",
+    "es6-promise": "^4.2.4",
     "eslint": "^5.1.0",
     "eslint-config-prettier": "^2.9.0",
     "eslint-plugin-import": "^2.13.0",
diff --git a/scripts/build-docs-data.js b/scripts/build-docs-data.js
index f258a3d..f4aa999 100755
--- a/scripts/build-docs-data.js
+++ b/scripts/build-docs-data.js
@@ -24,12 +24,15 @@ const excludeSourceDirs = new Set(['page-loading-indicator', 'utils']);
 
 function processExampleFile(filename) {
   return pify(fs.readFile)(filename, 'utf8').then(code => {
+    // Assumes there is only one block comment, and it is the description.
     const descriptionMatch = /\/\*([\s\S]+?)\*\//.exec(code);
     if (!descriptionMatch) {
       throw new Error(`No description found for example ${filename}`);
     }
     code = code
-      .replace(`from '../src/`, `from '@mapbox/mr-ui/`)
+      // Replace imported component paths with npm package paths.
+      .replace(/from '\.\.\/(\.\.\/)?/g, `from '@mapbox/mr-ui/`)
+      // Remove block comments (especially the description).
       .replace(/\/\*[\s\S]*?\*\/[\s]*/, '')
       .trim();
 
@@ -49,7 +52,7 @@ function processExampleFile(filename) {
 function getExamples(componentDirectory) {
   const examplesDirectory = path.join(componentDirectory, 'examples');
   return globby(path.join(examplesDirectory, '*.js')).then(filenames => {
-    return Promise.all(filenames.map(processExampleFile));
+    return Promise.all(filenames.sort().map(processExampleFile));
   });
 }
 
diff --git a/src/components/button/button.js b/src/components/button/button.js
index 4106830..ad8c0e3 100644
--- a/src/components/button/button.js
+++ b/src/components/button/button.js
@@ -11,7 +11,7 @@ import xtend from 'xtend';
  * render a different element altogether.)
  *
  * If you'd like to put an icon before or after the text of your button,
- * use [IconText](#mbxicontext) for the content.
+ * use [IconText](#icontext) for the content.
  */
 class Button extends React.Component {
   render() {
@@ -134,7 +134,7 @@ Button.propTypes = {
   /**
    * The button's content. A string is recommended, but you can put an element
    * in here if you think that's right. If you do, it should be inline-level,
-   * using `<span>`s instead of `<div>`s. ([IconText](#mbxicontext) is
+   * using `<span>`s instead of `<div>`s. ([IconText](#icontext) is
    * inline-level.)
    */
   children: PropTypes.node.isRequired,
@@ -184,6 +184,8 @@ Button.propTypes = {
   size: PropTypes.oneOf(['small', 'medium', 'large']),
   /**
    * The width of the button: `"small"`, `"medium"`, `"large"`, or `"full"`.
+   * For every option but `"full"` this prop determines horizontal padding.
+   * `"full"` sets the button to fill the width of its container.
    *
    * This is distinguished from `size` because buttons of the same general size
    * can vary by width.
@@ -207,11 +209,14 @@ Button.propTypes = {
   /**
    * If `true`, the element will be `block` displayed instead of `inline-block`.
    *
-   * This is sometimes necessary to get your pixel-perfect layout.
+   * This is sometimes necessary to get your pixel-perfect layout, if you don't
+   * want the extra line-height that wraps inline elements. Typically, you
+   * should only set `block` to `true` if the parent element is controlling
+   * width (in a layout that uses flexbox, absolute positioning, or floats).
    */
   block: PropTypes.bool,
   /**
-   * Is it disbaled?
+   * Is it disabled?
    */
   disabled: PropTypes.bool,
   /**
@@ -222,13 +227,11 @@ Button.propTypes = {
   component: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
   /**
    * An object of props that you want to pass through to the element that
-   * Button renders.  This can be useful if you want to disable
+   * Button renders. This can be useful if you want to disable
    * the button, assign an ID for testing, add an ARIA attribute, toss in some
    * custom style properties, etc.
    */
-  passthroughProps: PropTypes.objectOf(
-    PropTypes.oneOfType([PropTypes.string, PropTypes.bool, PropTypes.number])
-  )
+  passthroughProps: PropTypes.object
 };
 
 Button.defaultProps = {
diff --git a/src/components/button/examples/button-b.js b/src/components/button/examples/button-b.js
index 337bea2..bfe5505 100644
--- a/src/components/button/examples/button-b.js
+++ b/src/components/button/examples/button-b.js
@@ -1,5 +1,5 @@
 /*
-A slightly wider, outlined button with an alternate color, using [IconText](#mbxicontext) to prefix the text with an icon.
+A slightly wider, outlined button with an alternate color, using [IconText](#icontext) to prefix the text with an icon.
 */
 import React from 'react';
 import Button from '../button';
diff --git a/src/components/control-file/control-file.js b/src/components/control-file/control-file.js
index 57ec5e0..68dd65a 100644
--- a/src/components/control-file/control-file.js
+++ b/src/components/control-file/control-file.js
@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import omit from '../utils/omit';
-import IconButton from '../icon-button';
+import Tooltip from '../tooltip';
 import Icon from '../icon';
 import ControlLabel from '../control-label';
 import ControlWrapper from '../control-wrapper';
@@ -176,14 +176,16 @@ export default class ControlFile extends React.Component {
           </div>
           {this.state.displayValue && (
             <div className="flex-child">
-              <IconButton
-                icon="trash"
-                tooltipProps={{
-                  content: 'Clear'
-                }}
-                themeButton={`link link--gray relative bg-transparent py0 ${themeControlFileClear}`}
-                onClick={this.onClear}
-              />
+              <Tooltip content="Clear" block={true}>
+                <button
+                  type="button"
+                  className={`block link link--gray relative bg-transparent py0 ${themeControlFileClear}`}
+                  onClick={this.onClear}
+                  data-test="control-file-clear"
+                >
+                  <Icon name="trash" />
+                </button>
+              </Tooltip>
             </div>
           )}
         </div>
diff --git a/src/components/copiable/copiable.js b/src/components/copiable/copiable.js
index 52d5d90..11ea7ad 100644
--- a/src/components/copiable/copiable.js
+++ b/src/components/copiable/copiable.js
@@ -1,42 +1,33 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import classnames from 'classnames';
 import CopyButton from '../copy-button';
 import Popover from '../popover';
 import OSKey from 'os-key';
 import select from 'select';
-import Clipboard from 'clipboard/dist/clipboard.min.js';
-import debounce from 'debounce';
 import getWindow from '../utils/get-window';
 
 const DISABLE_CLICK_TO_SELECT_THRESHOLD = 640;
 const FEEDBACK_TIME = 2000;
 
+/**
+ * Make some text easily copiable.
+ *
+ * Comes with a [CopyButton](#copybutton) that you can click to automatically
+ * copy the text.
+ *
+ * Also, on wider viewports a click on the text will automatically select it
+ * all, so you can easily copy with your favorite keyboard shortcut.
+ */
 export default class Copiable extends React.Component {
-  static propTypes = {
-    /** The text that will be displayed and copied. */
-    value: PropTypes.string.isRequired,
-    /** Styling for the component wrapper. */
-    themeWrapper: PropTypes.string,
-    /** Styling for the copy button's container. */
-    themeCopyButtonContainer: PropTypes.string,
-    /** Styling for the copy button. */
-    themeCopyButton: PropTypes.string,
-    /** Styling for the text readout. */
-    themeText: PropTypes.string,
-    /** Styling for the copy hint tooltip. */
-    themeTooltip: PropTypes.string
-  };
-  static defaultProps = {
-    themeWrapper: 'relative round z0 scroll-styled',
-    themeCopyButtonContainer: 'absolute z2 top right mr3 mt3 color-white',
-    themeCopyButton: 'btn btn--xs py3 px3 round',
-    themeText: 'color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s'
-  };
+  constructor(props) {
+    super(props);
 
-  state = {
-    copyTooltipActive: false,
-    disableClickToSelect: false
-  };
+    this.copyHintText = '';
+    this.state = {
+      copyTooltipActive: false
+    };
+  }
 
   showCopyTooltip = () => {
     this.setState({ copyTooltipActive: true }, () => {
@@ -47,36 +38,19 @@ export default class Copiable extends React.Component {
   };
 
   componentDidMount() {
-    const { handleWindowResize } = this;
-    handleWindowResize();
-    window.addEventListener('resize', handleWindowResize);
-    this.copyHintText = `${this.getCopyKeys(
-      getWindow().navigator.userAgent
-    )} to copy!`;
+    this.copyHintText = (
+      <span>
+        <span className="txt-kbd">
+          {getCopyKeys(getWindow().navigator.userAgent)}
+        </span>{' '}
+        to copy
+      </span>
+    );
   }
 
-  componentWillUnmount = () => {
-    const { handleWindowResize, revertTimer } = this;
-    if (revertTimer) {
-      clearTimeout(revertTimer);
-    }
-    window.removeEventListener('resize', handleWindowResize);
-  };
-
-  handleWindowResize = debounce(() => {
-    const w = this.getWindowWidth();
-    if (
-      w <= DISABLE_CLICK_TO_SELECT_THRESHOLD &&
-      !this.state.disableClickToSelect
-    ) {
-      this.setState({ disableClickToSelect: true });
-    } else if (
-      w > DISABLE_CLICK_TO_SELECT_THRESHOLD &&
-      this.state.disableClickToSelect
-    ) {
-      this.setState({ disableClickToSelect: false });
-    }
-  }, 300);
+  componentWillUnmount() {
+    clearTimeout(this.revertTimer);
+  }
 
   setTextEl = el => {
     this.textEl = el;
@@ -90,72 +64,113 @@ export default class Copiable extends React.Component {
     return getWindow().innerWidth;
   }
 
-  getCopyKeys = ua => {
-    const keys = OSKey(ua);
-    if (!keys) return;
-    if (keys.primaryMeta) {
-      // ⌘+C
-      return `${keys.meta.symbol}+C`;
-    } else {
-      // Ctrl+C
-      return `${keys.ctrl.abbreviation}+C`;
-    }
-  };
-
-  handleSelect = () => {
-    if (this.state.disableClickToSelect) return;
+  handleTextFocus = () => {
+    if (getWindow().innerWidth < DISABLE_CLICK_TO_SELECT_THRESHOLD) return;
     select(this.getTextEl());
     this.showCopyTooltip();
   };
 
-  render() {
-    const {
-      value,
-      themeWrapper,
-      themeCopyButton,
-      themeCopyButtonContainer,
-      themeText,
-      themeTooltip
-    } = this.props;
-
-    const quickSelect = (
-      <span ref={this.setTextEl} onClick={this.handleSelect}>
-        {this.props.value}
-      </span>
-    );
+  handleTextBlur = event => {
+    if (!this.textEl.contains(event.relatedTarget)) {
+      this.setState({ copyTooltipActive: false });
+    }
+  };
 
-    const copyBtn = typeof document !== 'undefined' &&
-      Clipboard.isSupported() && (
-        <div className={themeCopyButtonContainer}>
-          <CopyButton
-            text={value}
-            iconButtonProps={{
-              themeButton: themeCopyButton
-            }}
-            textEl={this.textEl}
-          />
-        </div>
-      );
+  renderCopyButton() {
+    const { props } = this;
+
+    if (typeof window === 'undefined' || !CopyButton.isCopySupported()) {
+      return null;
+    }
+
+    return (
+      <div className="absolute top right px6 py6">
+        <CopyButton text={props.value} textEl={this.textEl} block={true} />
+      </div>
+    );
+  }
 
+  render() {
+    const { props } = this;
+    const copyButton = this.renderCopyButton();
     const copyHintPopover = this.state.copyTooltipActive && (
       <Popover
-        themePopover={themeTooltip}
         getAnchorElement={this.getTextEl}
         placement="top"
         alignment="center"
         hideWhenAnchorIsOffscreen={true}
-        accessibleTitle={this.copyHintText}
+        accessibleTitle="Copy the selected text"
+        padding="small"
       >
         <div className="txt-s">{this.copyHintText}</div>
       </Popover>
     );
 
+    const textClasses = classnames('my3 txt-mono txt-s mr24', {
+      'txt-truncate': props.truncated
+    });
+    const textStyle = {};
+    if (!props.truncated) {
+      textStyle.wordWrap = 'break-word';
+      textStyle.overflowWrap = 'break-word';
+    }
+
     return (
-      <div className={themeWrapper}>
-        <div className={themeText}>{quickSelect}</div>
-        {copyBtn}
+      <div className="relative clearfix bg-darken5 round">
+        {copyButton}
+        <div
+          tabIndex="-1"
+          ref={this.setTextEl}
+          onFocus={this.handleTextFocus}
+          onBlur={this.handleTextBlur}
+          className="py6 px12"
+          data-test="copiable-text-el"
+        >
+          <div className={textClasses} style={textStyle}>
+            {props.value}
+          </div>
+        </div>
         {copyHintPopover}
       </div>
     );
   }
 }
+
+Copiable.propTypes = {
+  /**
+   * The text that will be displayed and copied.
+   */
+  value: PropTypes.string.isRequired,
+  /**
+   * If `false` (default), the text will be overflow to multiple lines,
+   * and words longer than a single line (e.g. long access tokens or URLs)
+   * will be broken to enforce wrapping.
+   *
+   * If `true`, the Copiable's text will be truncated to a single line
+   * of text. **Only set this to `true` if you know that your target
+   * browsers support the copy button!** Some browsers will not effectively
+   * copy text that is truncated by CSS, so the risk is that some of your
+   * users might have *no way* to view and copy all the text if the copy
+   * button does not work for them.
+   *
+   * Horizontal scrolling is not an option because of things end up getting
+   * pretty gross across browsers.
+   */
+  truncated: PropTypes.bool
+};
+
+Copiable.defaultProps = {
+  truncated: false
+};
+
+function getCopyKeys(ua) {
+  const keys = OSKey(ua);
+  if (!keys) return;
+  if (keys.primaryMeta) {
+    // ⌘+C
+    return `${keys.meta.symbol}+C`;
+  } else {
+    // Ctrl+C
+    return `${keys.ctrl.abbreviation}+C`;
+  }
+}
diff --git a/src/components/copiable/examples/copiable-a.js b/src/components/copiable/examples/copiable-a.js
new file mode 100644
index 0000000..e9121c0
--- /dev/null
+++ b/src/components/copiable/examples/copiable-a.js
@@ -0,0 +1,23 @@
+/*
+All Copiables work the same way.
+*/
+import React from 'react';
+import Copiable from '../copiable';
+
+export default class Example extends React.Component {
+  render() {
+    return (
+      <div>
+        <div className="mb12">
+          <Copiable value="The length of this copiable text is to illustrate to you how this component's styling works when the text wraps to multiple lines" />
+        </div>
+        <div>
+          <Copiable
+            value="https://www.mapbox.com/something/special/for/you"
+            truncated={true}
+          />
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/src/components/copiable/examples/copiable-example-basic.js b/src/components/copiable/examples/copiable-example-basic.js
deleted file mode 100644
index a152842..0000000
--- a/src/components/copiable/examples/copiable-example-basic.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-Basic.
-*/
-import React from 'react';
-import Copiable from '../copiable';
-
-export default class Example extends React.Component {
-  state = { text: '' };
-
-  onCopy = text => {
-    this.setState({ text });
-  };
-
-  render() {
-    return (
-      <Copiable value="the text you copy the text you copy the text you copy the text you copy the text you copy" />
-    );
-  }
-}
diff --git a/src/components/copy-button/copy-button.js b/src/components/copy-button/copy-button.js
index 15cbbf7..48d24f0 100644
--- a/src/components/copy-button/copy-button.js
+++ b/src/components/copy-button/copy-button.js
@@ -1,47 +1,22 @@
 import React from 'react';
+import classnames from 'classnames';
 import PropTypes from 'prop-types';
-import xtend from 'xtend';
 import Clipboard from 'clipboard/dist/clipboard.min.js';
-import IconButton from '../icon-button';
 import Icon from '../icon';
 import Popover from '../popover';
-import omit from '../utils/omit';
-
+import Tooltip from '../tooltip';
+
+const FEEDBACK_TIME = 800;
+
+/**
+ * A button that, when clicked, copies the designated text to the clipboard.
+ *
+ * The static function `CopyButton.isCopySupported` returns a boolean
+ * indicating whether the current browser will support automatic copying.
+ * If it does not, you might want to hide your copy button and make sure
+ * the user is able to manually select and copy the text.
+ */
 export default class CopyButton extends React.PureComponent {
-  // If you add propsTypes, add the corresponding propNames.
-  static propTypes = {
-    /** The text that will be copied when the button is clicked. */
-    text: PropTypes.string.isRequired,
-    /**
-     * Pass in an element containing text to copy instead of the raw text to provide a better fallback,
-     * where the text is selected for you if execcopy is not supported
-     */
-    textEl: PropTypes.object,
-    /** The interval for which `Copied!` is displayed to the user. */
-    feedbackTime: PropTypes.number,
-    /**
-     * Invoked when the button is clicked.
-     * Passed one argument: the `text` prop.
-     */
-    onCopy: PropTypes.func,
-    /** Extra props to pass to the IconButton component. */
-    iconButtonProps: PropTypes.object
-  };
-  static propNames = [
-    'text',
-    'feedbackTime',
-    'onCopy',
-    'iconButtonProps',
-    'textEl'
-  ];
-
-  static defaultProps = {
-    feedbackTime: 800,
-    iconButtonProps: {
-      themeButton: 'btn--xs py3 px3 round'
-    }
-  };
-
   state = {
     showingFeedback: false
   };
@@ -53,7 +28,7 @@ export default class CopyButton extends React.PureComponent {
   }
 
   componentWillUnmount() {
-    if (this.revertTimer) clearTimeout(this.revertTimer);
+    clearTimeout(this.revertTimer);
     if (this.clipboard) {
       this.clipboard.destroy();
     }
@@ -72,7 +47,7 @@ export default class CopyButton extends React.PureComponent {
     this.setState({ showingFeedback: true });
     this.revertTimer = setTimeout(() => {
       this.setState({ showingFeedback: false });
-    }, this.props.feedbackTime);
+    }, FEEDBACK_TIME);
   };
 
   componentWillReceiveProps(nextProps) {
@@ -106,6 +81,27 @@ export default class CopyButton extends React.PureComponent {
     });
   };
 
+  renderFeedbackPopover() {
+    const { state } = this;
+    if (!state.showingFeedback) {
+      return null;
+    }
+
+    return (
+      <Popover
+        // We don't need this one to be as accessible as a regular tooltip
+        getAnchorElement={this.getContainer}
+        placement="top"
+        alignment="center"
+        hideWhenAnchorIsOffscreen={true}
+        accessibleTitle={null}
+        padding="small"
+      >
+        <div className="txt-s">Copied!</div>
+      </Popover>
+    );
+  }
+
   render() {
     const { props, state } = this;
     // don't render the button if you can't use it to copy,
@@ -114,67 +110,76 @@ export default class CopyButton extends React.PureComponent {
       return null;
     }
 
-    let tooltipText = state.showingFeedback ? 'Copied!' : 'Copy';
-    let icon = state.showingFeedback ? 'check' : 'clipboard';
-
-    const extraProps = omit(this.props, CopyButton.propNames);
-    let iconButtonProps = props.iconButtonProps;
-    let tooltipProps = {
-      content: tooltipText
-    };
-
-    if (props.iconButtonProps.tooltipProps) {
-      tooltipProps = xtend(tooltipProps, props.iconButtonProps.tooltipProps);
-    }
-    iconButtonProps.tooltipProps = tooltipProps;
-
-    let button = null;
-    if (state.showingFeedback) {
-      // When the action is complete, we switch from the tooltip-powered IconButton
-      // to a regular old button a regular old Popover. We do this so we can
-      // control the display of the Popover, for it to be there for the full
-      // this.props.feedbackTime.
-      button = (
-        <button
-          type="button"
-          className={`btn ${props.iconButtonProps.themeButton}`}
-          {...extraProps}
-        >
-          <Icon name={icon} />
-          <Popover
-            // We don't need this one to be as accessible as a regular tooltip
-            getAnchorElement={this.getContainer}
-            placement="top"
-            alignment="center"
-            hideWhenAnchorIsOffscreen={true}
-            accessibleTitle={null}
-          >
-            <span className="block txt-s">{tooltipText}</span>
-          </Popover>
-        </button>
-      );
-    } else {
-      button = (
-        <IconButton
-          onClick={this.handleCopyButtonClick}
-          icon={icon}
-          {...iconButtonProps}
-          {...extraProps}
-        />
-      );
-    }
+    const iconName = state.showingFeedback ? 'check' : 'clipboard';
+    const buttonClasses = classnames(props.className, {
+      block: props.block
+    });
 
     // data-clipboard-text and the container ref are used by clipboard.js
     // to copy text if you do not pass in a textEl as a prop.
     // Note that this wont have as nice a failure mode.
     return (
-      <div
-        ref={this.setContainer}
-        className="inline-block"
-        data-clipboard-text={this.props.text}
+      <Tooltip
+        disabled={state.showingFeedback}
+        content="Copy"
+        block={props.block}
       >
-        {button}
-      </div>
+        <button
+          type="button"
+          className={buttonClasses}
+          {...props.passthroughProps}
+          ref={this.setContainer}
+          data-clipboard-text={props.text}
+          onClick={this.handleCopyButtonClick}
+        >
+          <Icon name={iconName} />
+          {this.renderFeedbackPopover()}
+        </button>
+      </Tooltip>
     );
   }
 }
+
+CopyButton.propTypes = {
+  /**
+   * The text that will be copied when the button is clicked.
+   */
+  text: PropTypes.string.isRequired,
+  /**
+   * Pass in an element containing text to copy instead of the raw text
+   * to provide a better fallback, where the text is selected for you if
+   * `execcopy` is not supported.
+   */
+  textEl: PropTypes.object,
+  /**
+   * Invoked when the button is clicked.
+   * Passed one argument: the `text` prop.
+   */
+  onCopy: PropTypes.func,
+  /**
+   * If `true`, the element will be `block` displayed instead of `inline-block`.
+   *
+   * This is sometimes necessary to get your pixel-perfect layout, if you don't
+   * want the extra line-height that wraps inline elements. Typically, you
+   * should only set `block` to `true` if the parent element is controlling
+   * width (in a layout that uses flexbox, absolute positioning, or floats).
+   */
+  block: PropTypes.bool,
+  /**
+   * The `className` prop of the `<button>`.
+   */
+  className: PropTypes.string,
+  /**
+   * An object of props that you want to pass through to the `<button>`.
+   */
+  passthroughProps: PropTypes.object
+};
+
+CopyButton.defaultProps = {
+  className: 'btn btn--xs py3 px3 round',
+  block: false
+};
+
+CopyButton.isCopySupported = () => {
+  return Clipboard.isSupported();
+};
diff --git a/src/components/copy-button/examples/copy-button-a.js b/src/components/copy-button/examples/copy-button-a.js
new file mode 100644
index 0000000..1804b03
--- /dev/null
+++ b/src/components/copy-button/examples/copy-button-a.js
@@ -0,0 +1,23 @@
+/*
+Defaults.
+*/
+import React from 'react';
+import CopyButton from '../copy-button';
+
+export default class Example extends React.Component {
+  render() {
+    return (
+      <div className="flex-parent flex-parent--center-cross">
+        <div className="flex-child mr24">
+          <CopyButton text="Hello explorer" block={true} />
+        </div>
+        <div className="flex-child flex-child--grow">
+          <input
+            className="input input--s wmax360"
+            placeholder="Paste text here"
+          />
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/src/components/copy-button/examples/copy-button-b.js b/src/components/copy-button/examples/copy-button-b.js
new file mode 100644
index 0000000..10d0a85
--- /dev/null
+++ b/src/components/copy-button/examples/copy-button-b.js
@@ -0,0 +1,26 @@
+/*
+Custom styling.
+*/
+import React from 'react';
+import CopyButton from '../copy-button';
+
+export default class Example extends React.Component {
+  render() {
+    return (
+      <div className="flex-parent flex-parent--center-cross">
+        <div className="flex-child mr24">
+          <CopyButton
+            text="Hello fancy explorer"
+            className="px6 py6 btn btn--purple btn--stroke"
+          />
+        </div>
+        <div className="flex-child flex-child--grow">
+          <input
+            className="input input--s wmax360"
+            placeholder="Paste text here"
+          />
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/src/components/copy-button/examples/copy-button-example-basic.js b/src/components/copy-button/examples/copy-button-example-basic.js
deleted file mode 100644
index 659f9c3..0000000
--- a/src/components/copy-button/examples/copy-button-example-basic.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-Basic.
-*/
-import React from 'react';
-import CopyButton from '../copy-button';
-
-export default class Example extends React.Component {
-  state = { text: '' };
-
-  onCopy = text => {
-    this.setState({ text });
-  };
-
-  render() {
-    return (
-      <CopyButton
-        text="Hello explorer"
-        feedbackTime={500}
-        onCopy={this.onCopy}
-      />
-    );
-  }
-}
diff --git a/src/components/copy-button/examples/copy-button-example-options.js b/src/components/copy-button/examples/copy-button-example-options.js
deleted file mode 100644
index e535ea1..0000000
--- a/src/components/copy-button/examples/copy-button-example-options.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-Options.
-*/
-import React from 'react';
-import CopyButton from '../copy-button';
-
-export default class Example extends React.Component {
-  state = { text: '' };
-
-  onCopy = text => {
-    this.setState({ text });
-  };
-
-  render() {
-    return (
-      <CopyButton
-        text="Hello explorer"
-        feedbackTime={500}
-        onCopy={this.onCopy}
-        iconButtonProps={{
-          themeButton: 'btn--xs py3 px3 round bg-pink',
-          tooltipProps: {
-            content: 'Copy?'
-          }
-        }}
-      />
-    );
-  }
-}
diff --git a/src/components/icon-button/examples/icon-button-example-basic.js b/src/components/icon-button/examples/icon-button-example-basic.js
deleted file mode 100644
index 16506bc..0000000
--- a/src/components/icon-button/examples/icon-button-example-basic.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-Basic.
-*/
-import React from 'react';
-import IconButton from '../icon-button';
-
-export default class Example extends React.Component {
-  render() {
-    return (
-      <IconButton
-        onClick={() => {
-          /* console.log('triggered on click') */
-        }}
-        icon="close"
-        tooltipProps={{
-          content: 'Close'
-        }}
-      />
-    );
-  }
-}
diff --git a/src/components/icon-button/examples/icon-button-example-options.js b/src/components/icon-button/examples/icon-button-example-options.js
deleted file mode 100644
index 65e73f9..0000000
--- a/src/components/icon-button/examples/icon-button-example-options.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
-Options.
-*/
-import React from 'react';
-import IconButton from '../icon-button';
-
-export default class Example extends React.Component {
-  render() {
-    return (
-      <IconButton
-        onClick={() => {
-          /* console.log('triggered on click') */
-        }}
-        icon="bike"
-        themeButton="py12 px12 bg-gray"
-        themeIcon="color-gray-dark"
-        tooltipProps={{
-          content: <strong className="txt-bold">tada</strong>,
-          placement: 'right'
-        }}
-      />
-    );
-  }
-}
diff --git a/src/components/icon-button/icon-button.js b/src/components/icon-button/icon-button.js
deleted file mode 100644
index ab3de8d..0000000
--- a/src/components/icon-button/icon-button.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import xtend from 'xtend';
-import Tooltip from '../tooltip';
-import Icon from '../icon';
-import omit from '../utils/omit';
-
-export default class IconButton extends React.PureComponent {
-  // If you change propTypes, also change propNames.
-  static propTypes = {
-    /** Callback when button is clicked. */
-    onClick: PropTypes.func.isRequired,
-    /** Name of the icon to use. */
-    icon: PropTypes.string.isRequired,
-    /** Extra props to pass to the Tooltip component. tooltipProps.content is required. */
-    tooltipProps: PropTypes.object,
-    /** Assembly classes to apply to the button element */
-    themeButton: PropTypes.string,
-    /** Assembly classes to apply to the icon element */
-    themeIcon: PropTypes.string
-  };
-
-  static propNames = [
-    'onClick',
-    'icon',
-    'tooltipProps',
-    'themeButton',
-    'themeIcon'
-  ];
-
-  static defaultProps = {
-    themeButton: 'btn--xs px0'
-  };
-
-  getBody = (triggerProps: Object) => {
-    const themeButton = `btn ${this.props.themeButton}`;
-    const extraProps = omit(this.props, IconButton.propNames);
-    const buttonProps = xtend(extraProps, triggerProps, {
-      type: 'button',
-      className: themeButton,
-      onClick: this.props.onClick
-    });
-    const iconProps = {
-      name: this.props.icon
-    };
-    if (this.props.themeIcon !== undefined) {
-      iconProps.themeIcon = this.props.themeIcon;
-    }
-
-    return (
-      <button {...buttonProps}>
-        <Icon {...iconProps} />
-      </button>
-    );
-  };
-
-  render() {
-    return <Tooltip {...this.props.tooltipProps}>{this.getBody}</Tooltip>;
-  }
-}
diff --git a/src/components/icon-button/index.js b/src/components/icon-button/index.js
deleted file mode 100644
index 5c8cb19..0000000
--- a/src/components/icon-button/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import main from './icon-button';
-
-export default main;
diff --git a/src/components/icon/examples/icon-example-basic.js b/src/components/icon/examples/icon-a.js
similarity index 90%
rename from src/components/icon/examples/icon-example-basic.js
rename to src/components/icon/examples/icon-a.js
index 9258a07..606a5af 100644
--- a/src/components/icon/examples/icon-example-basic.js
+++ b/src/components/icon/examples/icon-a.js
@@ -1,5 +1,5 @@
 /*
-Basic.
+Basic Icon usage.
 */
 import React from 'react';
 import Icon from '../icon';
diff --git a/src/components/icon/examples/icon-b.js b/src/components/icon/examples/icon-b.js
new file mode 100644
index 0000000..8493acb
--- /dev/null
+++ b/src/components/icon/examples/icon-b.js
@@ -0,0 +1,39 @@
+/*
+Icons rendered inline with multi-line text.
+*/
+import React from 'react';
+import Icon from '../icon';
+
+export default class Example extends React.Component {
+  render() {
+    return (
+      <div style={{ lineHeight: '24px' }}>
+        The last thing you want in your kitchen is a{' '}
+        <span className="inline-block color-red">
+          <Icon name="flame" inline={true} /> fire
+        </span>{' '}
+        even though there is{' '}
+        <span className="inline-block color-blue">
+          <Icon name="water" inline={true} /> water
+        </span>{' '}
+        nearby. The last thing you want in your kitchen is a{' '}
+        <span className="inline-block color-red">
+          <Icon name="flame" inline={true} /> fire
+        </span>{' '}
+        even though there is{' '}
+        <span className="inline-block color-blue">
+          <Icon name="water" inline={true} /> water
+        </span>{' '}
+        nearby. The last thing you want in your kitchen is a{' '}
+        <span className="inline-block color-red">
+          <Icon name="flame" inline={true} /> fire
+        </span>{' '}
+        even though there is{' '}
+        <span className="inline-block color-blue">
+          <Icon name="water" inline={true} /> water
+        </span>{' '}
+        nearby.
+      </div>
+    );
+  }
+}
diff --git a/src/components/icon/examples/icon-example-options.js b/src/components/icon/examples/icon-example-options.js
deleted file mode 100644
index 290a13a..0000000
--- a/src/components/icon/examples/icon-example-options.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-Options.
-*/
-import React from 'react';
-import Icon from '../icon';
-
-export default class Example extends React.Component {
-  render() {
-    return (
-      <Icon
-        name="antialias"
-        inlineHeading={true}
-        theme={{
-          icon: 'icon--l'
-        }}
-      />
-    );
-  }
-}
diff --git a/src/components/icon/icon.js b/src/components/icon/icon.js
index 52c1ed2..21869b9 100644
--- a/src/components/icon/icon.js
+++ b/src/components/icon/icon.js
@@ -1,29 +1,36 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import omit from '../utils/omit';
 import getWindow from '../utils/get-window';
+import shallowEqualObjects from '../utils/shallow-equal-objects';
 
-export default class Icon extends React.PureComponent {
-  // If you change propTypes, make sure to change propNames.
-  static propTypes = {
-    name: PropTypes.string.isRequired,
-    themeIcon: PropTypes.string,
-    inline: PropTypes.bool
-  };
-  static propNames = ['name', 'themeIcon', 'inline'];
+/**
+ * Display an Assembly icon.
+ *
+ * Besides providing a convenient shortcut, this component does the following:
+ *
+ * - Sets some accessibility props.
+ * - Provides an `inline` mode that automatically sizes icons to match their
+ *   surrounding text.
+ */
+export default class Icon extends React.Component {
+  componentDidMount() {
+    this.setHeight();
+  }
 
-  static defaultProps = {
-    inline: false
-  };
+  shouldComponentUpdate(nextProps) {
+    return !shallowEqualObjects(this.props, nextProps, ['passthroughProps']);
+  }
 
-  componentDidMount() {
+  componentDidUpdate() {
+    this.setHeight();
+  }
+
+  setHeight() {
     if (this.props.inline && this.iconElement) {
-      // Match height of icon element to line height, ensuring inlined icon
-      // doesn't displace text line height, is vertically centered,
-      // and wraps along with multi-line text.
-      this.iconElement.style.height = getWindow().getComputedStyle(
-        this.iconElement
-      )['line-height'];
+      const lineHeight = getWindow().getComputedStyle(this.iconElement)[
+        'line-height'
+      ];
+      this.iconElement.style.height = lineHeight;
     }
   }
 
@@ -35,19 +42,16 @@ export default class Icon extends React.PureComponent {
     const { props } = this;
 
     let iconClasses = 'events-none icon';
-    if (props.themeIcon !== undefined) iconClasses += ` ${props.themeIcon}`;
-
     if (props.inline) {
       iconClasses += ' inline-block align-t';
     }
 
-    const extraProps = omit(props, Icon.propNames.concat('style'));
-    const svgStyle = props.style || {};
-    if (!svgStyle.width) {
-      svgStyle.width = 18;
+    const svgStyle = props.passthroughProps.style || {};
+    if (!svgStyle.width && props.size) {
+      svgStyle.width = props.size;
     }
-    if (!svgStyle.height) {
-      svgStyle.height = 18;
+    if (!svgStyle.height && props.size) {
+      svgStyle.height = props.size;
     }
 
     const iconContent = (
@@ -56,8 +60,8 @@ export default class Icon extends React.PureComponent {
         role="presentation"
         focusable="false"
         className={iconClasses}
+        {...props.passthroughProps}
         style={svgStyle}
-        {...extraProps}
       >
         <use
           xmlnsXlink="http://www.w3.org/1999/xlink"
@@ -69,3 +73,46 @@ export default class Icon extends React.PureComponent {
     return iconContent;
   }
 }
+
+Icon.propTypes = {
+  /**
+   * The name of the [Assembly icon](https://www.mapbox.com/assembly/icons/) that
+   * you want to display.
+   */
+  name: PropTypes.string.isRequired,
+  /**
+   * The width and height of the icon. All icons fill up a square space,
+   * so this value will be applied to both width and height.
+   *
+   * If `inline: true`, the technical height will be controlled by the
+   * line-height of the container, but the appearance of the icon will still
+   * be in accordance with your `size` value (because it's limited by the
+   * width).
+   */
+  size: PropTypes.number,
+  /**
+   * If `true`, the icon will be adjusted after mounting so that its height
+   * matches the line-height of its container. The result of this is that
+   * the icon will not displace the text's established line-height and
+   * will be vertically centered with the text alongside it.
+   *
+   * This is most useful when you are inserting the icon within multiline text,
+   * so you can't use a flexbox layout to vertically center the icon and text.
+   *
+   * Be aware that there are edge cases that can cause problems with this
+   * setting. For example, it won't work well if you don't have a `line-height`
+   * set to a pixel value, or if there are dynamic adjustments to the
+   * line-height.
+   */
+  inline: PropTypes.bool,
+  /**
+   * Props to pass directly to the `<svg>` element.
+   */
+  passthroughProps: PropTypes.object
+};
+
+Icon.defaultProps = {
+  inline: false,
+  passthroughProps: {},
+  size: 18
+};
diff --git a/src/components/modal/examples/modal-b.js b/src/components/modal/examples/modal-b.js
index e9fa776..902f6af 100644
--- a/src/components/modal/examples/modal-b.js
+++ b/src/components/modal/examples/modal-b.js
@@ -30,7 +30,7 @@ export default class Example extends React.Component {
       <Modal
         accessibleTitle="Animals"
         onExit={this.closeModal}
-        padded={false}
+        padding="none"
         size="small"
         initialFocus="#modal-input"
       >
diff --git a/src/components/modal/modal.js b/src/components/modal/modal.js
index 328dd32..1c4ecd8 100644
--- a/src/components/modal/modal.js
+++ b/src/components/modal/modal.js
@@ -2,7 +2,8 @@ import React from 'react';
 import classnames from 'classnames';
 import PropTypes from 'prop-types';
 import AriaModal from 'react-aria-modal';
-import IconButton from '../icon-button';
+import Tooltip from '../tooltip';
+import Icon from '../icon';
 import getWindow from '../utils/get-window';
 import ModalActions from './modal-actions';
 
@@ -37,7 +38,7 @@ export default class Modal extends React.Component {
       if (offsetParent.tagName === 'BODY' || offsetParent.tagName === 'HTML') {
         return;
       }
-      offsetParent.scrollTo(0, 0);
+      offsetParent.scrollTop = 0;
     }, 0);
   }
 
@@ -74,14 +75,16 @@ export default class Modal extends React.Component {
     if (props.onExit) {
       closeButton = (
         <div className="absolute top right">
-          <IconButton
-            icon="close"
-            onClick={props.onExit}
-            tooltipProps={{
-              content: 'Close'
-            }}
-            themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-          />
+          <Tooltip block={true} content="Close">
+            <button
+              type="button"
+              className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+              onClick={props.onExit}
+              data-test="modal-close"
+            >
+              <Icon name="close" />
+            </button>
+          </Tooltip>
         </div>
       );
     }
@@ -95,9 +98,7 @@ export default class Modal extends React.Component {
 
     const containerClasses = classnames(
       `relative wmax-full ${widthClass} bg-white round`,
-      {
-        'px36 py36': props.padded
-      }
+      { 'px36 py36': props.padding === 'large' }
     );
 
     const dialogBody = (
@@ -173,9 +174,9 @@ Modal.propTypes = {
    */
   children: PropTypes.node.isRequired,
   /**
-   * If `false`, the modal will not have its default padding.
+   * `'large'` or `'none'`.
    */
-  padded: PropTypes.bool,
+  padding: PropTypes.oneOf(['large', 'none']),
   /**
    * If `true`, the modal will have the accessibility props of an alert modal.
    * (Only affects screen readers.)
@@ -237,5 +238,5 @@ Modal.propTypes = {
 Modal.defaultProps = {
   size: 'large',
   getApplicationNode: () => document.getElementById('app'),
-  padded: true
+  padding: 'large'
 };
diff --git a/src/components/popover-trigger/examples/popover-trigger-a.js b/src/components/popover-trigger/examples/popover-trigger-a.js
index d3fa331..c46bb80 100644
--- a/src/components/popover-trigger/examples/popover-trigger-a.js
+++ b/src/components/popover-trigger/examples/popover-trigger-a.js
@@ -3,6 +3,7 @@ A standard PopoverTrigger.
 */
 import React from 'react';
 import PopoverTrigger from '../popover-trigger';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   getPopoverContent = () => {
@@ -13,9 +14,7 @@ export default class Example extends React.Component {
     return (
       <div>
         <PopoverTrigger content={this.getPopoverContent}>
-          <button type="button" className="btn btn--s btn--purple">
-            Trigger
-          </button>
+          <Button size="medium">Trigger</Button>
         </PopoverTrigger>
       </div>
     );
diff --git a/src/components/popover-trigger/examples/popover-trigger-b.js b/src/components/popover-trigger/examples/popover-trigger-b.js
index a95b821..fa38039 100644
--- a/src/components/popover-trigger/examples/popover-trigger-b.js
+++ b/src/components/popover-trigger/examples/popover-trigger-b.js
@@ -3,6 +3,7 @@ A PopoverTrigger that opens a dark popover and traps focus inside its content.
 */
 import React from 'react';
 import PopoverTrigger from '../popover-trigger';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   constructor(props) {
@@ -48,9 +49,7 @@ export default class Example extends React.Component {
             'data-test': 'trigger-container'
           }}
         >
-          <button type="button" className="btn btn--s">
-            Trigger
-          </button>
+          <Button size="medium">Trigger</Button>
         </PopoverTrigger>
       </div>
     );
diff --git a/src/components/popover-trigger/popover-trigger.js b/src/components/popover-trigger/popover-trigger.js
index ce78fb0..44bc70f 100644
--- a/src/components/popover-trigger/popover-trigger.js
+++ b/src/components/popover-trigger/popover-trigger.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import classnames from 'classnames';
 import hoverintent from 'hoverintent';
 import Popover from '../popover';
 
@@ -53,6 +54,10 @@ export default class PopoverTrigger extends React.Component {
     ) {
       popoverElement.addEventListener('mouseleave', this.onPopoverMouseLeave);
     }
+
+    if (!prevProps.disabled && this.props.disabled) {
+      this.hide();
+    }
   }
 
   componentWillUnmount() {
@@ -366,10 +371,15 @@ export default class PopoverTrigger extends React.Component {
       );
     }
 
+    const classes = classnames({
+      block: props.block,
+      'inline-block': !props.block
+    });
+
     return (
       <div
         ref={this.setTriggerElement}
-        style={{ display: props.display }}
+        className={classes}
         {...props.passthroughTriggerProps}
         onClick={this.onAnyClick}
         onFocus={this.onTriggerFocus}
@@ -390,10 +400,11 @@ PopoverTrigger.propTypes = {
    */
   children: PropTypes.node.isRequired,
   /**
-   * Accepts a CSS `display` value for the trigger: `'inline-block'` or
-   * `'block'`.
+   * If `true`, the element will be `block` displayed instead of `inline-block`.
+   *
+   * This is sometimes necessary to get your pixel-perfect layout.
    */
-  display: PropTypes.oneOf(['inline-block', 'block']),
+  block: PropTypes.bool,
   /**
    * The popover content. This can either be a string, valid JSX, or a function
    * returning either.
@@ -463,7 +474,7 @@ PopoverTrigger.propTypes = {
 };
 
 PopoverTrigger.defaultProps = {
-  display: 'inline-block',
+  block: false,
   disabled: false,
   renderHiddenContent: false,
   receiveFocus: true,
diff --git a/src/components/popover/examples/popover-a.js b/src/components/popover/examples/popover-a.js
index 95ebcb0..387c78e 100644
--- a/src/components/popover/examples/popover-a.js
+++ b/src/components/popover/examples/popover-a.js
@@ -3,6 +3,7 @@ Standard popover.
 */
 import React from 'react';
 import Popover from '../popover';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   constructor(props) {
@@ -49,13 +50,15 @@ export default class Example extends React.Component {
   render() {
     return (
       <div>
-        <button
-          ref={this.setAnchor}
+        <Button
           onClick={this.togglePopover}
-          className="btn btn--s"
+          size="medium"
+          passthroughProps={{
+            ref: this.setAnchor
+          }}
         >
           Toggle popover
-        </button>
+        </Button>
         {this.renderPopover()}
       </div>
     );
diff --git a/src/components/popover/examples/popover-b.js b/src/components/popover/examples/popover-b.js
index 26e3379..524e0fa 100644
--- a/src/components/popover/examples/popover-b.js
+++ b/src/components/popover/examples/popover-b.js
@@ -3,6 +3,7 @@ Dark popover.
 */
 import React from 'react';
 import Popover from '../popover';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   constructor(props) {
@@ -50,13 +51,15 @@ export default class Example extends React.Component {
   render() {
     return (
       <div>
-        <button
-          ref={this.setAnchor}
+        <Button
           onClick={this.togglePopover}
-          className="btn btn--s"
+          size="medium"
+          passthroughProps={{
+            ref: this.setAnchor
+          }}
         >
           Toggle popover
-        </button>
+        </Button>
         {this.renderPopover()}
       </div>
     );
diff --git a/src/components/popover/examples/popover-c.js b/src/components/popover/examples/popover-c.js
index 695e7e0..f688fe2 100644
--- a/src/components/popover/examples/popover-c.js
+++ b/src/components/popover/examples/popover-c.js
@@ -3,6 +3,7 @@ Warning popover.
 */
 import React from 'react';
 import Popover from '../popover';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   constructor(props) {
@@ -50,13 +51,15 @@ export default class Example extends React.Component {
   render() {
     return (
       <div>
-        <button
-          ref={this.setAnchor}
+        <Button
           onClick={this.togglePopover}
-          className="btn btn--s"
+          size="medium"
+          passthroughProps={{
+            ref: this.setAnchor
+          }}
         >
           Toggle popover
-        </button>
+        </Button>
         {this.renderPopover()}
       </div>
     );
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index 1cad1f7..40bed92 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -1,4 +1,5 @@
 import React from 'react';
+import classnames from 'classnames';
 import PropTypes from 'prop-types';
 import createFocusTrap from 'focus-trap';
 import tabbable from 'tabbable';
@@ -186,10 +187,13 @@ export default class Popover extends React.Component {
   render() {
     const { props } = this;
     const colors = getColors(props.coloring);
-    let bodyClasses = `${colors.textClass} shadow-darken25 round`;
-    if (props.padded) {
-      bodyClasses += ' px12 py12';
-    }
+    const bodyClasses = classnames(
+      `${colors.textClass} shadow-darken25 round`,
+      {
+        'px12 py12': props.padding === 'medium',
+        'px12 py6': props.padding === 'small'
+      }
+    );
 
     return (
       <PopoverPositioner
@@ -251,10 +255,9 @@ Popover.propTypes = {
    */
   coloring: PropTypes.oneOf(['light', 'dark', 'warning']),
   /**
-   * Set to `false` to remove the default padding on the popover body
-   * (and, hopefully, apply your own).
+   * `'medium'`, `'small'`, or `'none'`.
    */
-  padded: PropTypes.bool,
+  padding: PropTypes.oneOf(['medium', 'small', 'none']),
   /**
    * Whether or not the popover has a triangle pointer.
    */
@@ -337,7 +340,7 @@ Popover.defaultProps = {
   coloring: 'light',
   placement: 'right',
   alignment: 'top',
-  padded: true,
+  padding: 'medium',
   hasPointer: true,
   hideWhenAnchorIsOffscreen: false,
   allowPlacementAxisChange: true,
diff --git a/src/components/tooltip/examples/tooltip-a.js b/src/components/tooltip/examples/tooltip-a.js
index 15f1e75..866e65a 100644
--- a/src/components/tooltip/examples/tooltip-a.js
+++ b/src/components/tooltip/examples/tooltip-a.js
@@ -3,12 +3,13 @@ A standard tooltip.
 */
 import React from 'react';
 import Tooltip from '../tooltip';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   render() {
     return (
       <Tooltip content="Here's your tooltip">
-        <button className="btn">Basic</button>
+        <Button size="medium">Basic</Button>
       </Tooltip>
     );
   }
diff --git a/src/components/tooltip/tooltip.js b/src/components/tooltip/tooltip.js
index b3b83db..f17d318 100644
--- a/src/components/tooltip/tooltip.js
+++ b/src/components/tooltip/tooltip.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import classnames from 'classnames';
 import PropTypes from 'prop-types';
+import Button from '../button';
 import PopoverTrigger from '../popover-trigger';
 
 let tooltipCounter = 0;
@@ -55,7 +56,7 @@ export default class Tooltip extends React.Component {
     if (children.type && typeof children.type === 'string') {
       return React.cloneElement(children, triggerProps);
     }
-    if (children.type && children.type.name === 'Button') {
+    if (children.type && children.type === Button) {
       return React.cloneElement(children, { passthroughProps: triggerProps });
     }
     throw new Error(
@@ -77,7 +78,6 @@ export default class Tooltip extends React.Component {
     }
 
     const bodyClasses = classnames({
-      'px12 py6': props.padded,
       'txt-s': props.textSize === 's',
       'txt-xs': props.textSize === 'xs',
       wmax120: props.maxWidth === 'small',
@@ -91,7 +91,7 @@ export default class Tooltip extends React.Component {
         content={content}
         renderHiddenContent={true}
         disabled={props.disabled}
-        display={props.display}
+        block={props.block}
         respondsToClick={props.respondsToClick}
         respondsToHover={true}
         respondsToFocus={true}
@@ -101,7 +101,7 @@ export default class Tooltip extends React.Component {
           placement: props.placement,
           alignment: props.alignment,
           coloring: props.coloring,
-          padded: false,
+          padding: props.padding,
           hideWhenAnchorIsOffscreen: true,
           passthroughProps: popoverPassthroughProps
         }}
@@ -157,18 +157,22 @@ Tooltip.propTypes = {
    */
   maxWidth: PropTypes.oneOf(['small', 'medium', 'none']),
   /**
-   * Set to `false` to remove the default padding on the popover body
-   * (and, hopefully, apply your own).
+   * `'small'` or `'none'`.
    */
-  padded: PropTypes.bool,
+  padding: PropTypes.oneOf(['small', 'none']),
   /**
    * If `true`, tooltip can be opened with a mouse click.
    */
   respondsToClick: PropTypes.bool,
   /**
-   * The CSS `display` value of the trigger element: '`block'` or '`inline-block'`.
+   * If `true`, the element will be `block` displayed instead of `inline-block`.
+   *
+   * This is sometimes necessary to get your pixel-perfect layout, if you don't
+   * want the extra line-height that wraps inline elements. Typically, you
+   * should only set `block` to `true` if the parent element is controlling
+   * width (in a layout that uses flexbox, absolute positioning, or floats).
    */
-  display: PropTypes.oneOf(['block', 'inline-block']),
+  block: PropTypes.bool,
   /**
    * Added as `data-test` to the tooltip element.
    */
@@ -181,8 +185,8 @@ Tooltip.defaultProps = {
   coloring: 'light',
   disabled: false,
   respondsToClick: false,
-  padded: true,
-  display: 'inline-block',
+  padding: 'small',
+  block: false,
   textSize: 's',
   maxWidth: 'medium'
 };
diff --git a/src/docs/components/component-example.js b/src/docs/components/component-example.js
index 8d4f8a0..8057314 100644
--- a/src/docs/components/component-example.js
+++ b/src/docs/components/component-example.js
@@ -15,7 +15,7 @@ export default class ComponentExample extends React.Component {
       return null;
     }
     return (
-      <pre className="my0 round-tl pre language-jsx">
+      <pre className="my0 round-tl pre language-jsx unround-tr unround-b">
         <code dangerouslySetInnerHTML={{ __html: this.props.code }} />
       </pre>
     );
diff --git a/src/docs/components/component-section.js b/src/docs/components/component-section.js
index 0013710..0b1a28b 100644
--- a/src/docs/components/component-section.js
+++ b/src/docs/components/component-section.js
@@ -33,7 +33,7 @@ export default class ComponentSection extends React.Component {
 
     const exampleEls = data.examples.map((example, i) => {
       return (
-        <div key={i} className="my24">
+        <div key={i} className="mb12">
           <ComponentExample {...example} />
         </div>
       );
@@ -41,7 +41,7 @@ export default class ComponentSection extends React.Component {
 
     return (
       <div className="mt24 mb12">
-        <h3 className="txt-h4 txt-bold pb6">Examples</h3>
+        <h3 className="txt-h4 txt-bold mb12">Examples</h3>
         {exampleEls}
       </div>
     );
diff --git a/src/docs/pages/index.js b/src/docs/pages/index.js
index 31e74a1..521345f 100644
--- a/src/docs/pages/index.js
+++ b/src/docs/pages/index.js
@@ -9,14 +9,15 @@ export default class Page extends React.Component {
         <Helmet>
           <meta charSet="utf-8" />
           <meta name="viewport" content="width=device-width,initial-scale=1" />
+          <meta name="robots" content="noindex" />
           <title>Mr. UI</title>
           <link
-            href="https://api.mapbox.com/mapbox-assembly/mbx/v0.26.0/assembly.min.css"
+            href="https://api.mapbox.com/mapbox-assembly/mbx/v0.27.0/assembly.min.css"
             rel="stylesheet"
           />
           <script
             async
-            src="https://api.mapbox.com/mapbox-assembly/mbx/v0.26.0/assembly.js"
+            src="https://api.mapbox.com/mapbox-assembly/mbx/v0.27.0/assembly.js"
           />
         </Helmet>
         <App />
diff --git a/src/docs/prism-theme.css b/src/docs/prism-theme.css
new file mode 100644
index 0000000..b9e2f42
--- /dev/null
+++ b/src/docs/prism-theme.css
@@ -0,0 +1,109 @@
+code[class*="language-"],
+pre[class*="language-"] {
+    color: #273d56;
+    direction: ltr;
+    text-align: left;
+    white-space: pre;
+    word-spacing: normal;
+    word-break: normal;
+
+    -moz-tab-size: 2;
+    -o-tab-size: 2;
+    tab-size: 2;
+
+    -webkit-hyphens: none;
+    -moz-hyphens: none;
+    -ms-hyphens: none;
+    hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
+    background: #aab7ef;
+}
+
+pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
+code[class*="language-"]::selection, code[class*="language-"] ::selection {
+    background: #aab7ef;
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+    /* padding: 1em; */
+    margin: .5em 0;
+    overflow: auto;
+    border: 1px solid #dddddd;
+    /* background-color: white; */
+}
+
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+    padding: .2em;
+    padding-top: 1px; padding-bottom: 1px;
+    background: #f8f8f8;
+    border: 1px solid #dddddd;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+    color: #999988; font-style: italic;
+}
+
+.token.namespace {
+    opacity: .7;
+}
+
+.token.tag .token.tag,
+.token.selector {
+    color: #7753eb;
+}
+
+.token.string,
+.token.attr-value {
+    color: #ee4e8b;
+}
+.token.punctuation,
+.token.operator {
+    color: #607d9c; /* no highlight */
+}
+
+.token.entity,
+.token.url,
+.token.symbol,
+.token.number,
+.token.boolean,
+.token.variable,
+.token.constant,
+.token.property,
+.token.regex,
+.token.inserted {
+    color: #33c377;
+}
+
+.token.atrule,
+.token.keyword,
+.language-autohotkey .token.selector {
+    color: #314ccd;
+}
+
+.token.function,
+.token.deleted,
+.language-autohotkey .token.tag {
+    color: #ba3b3f;
+}
+
+.token.important,
+.token.function,
+.token.bold {
+    font-weight: bold;
+}
+
+.token.italic {
+    font-style: italic;
+}


Test patch:
diff --git a/src/components/chevronous-text/__tests__/__snapshots__/chevronous-text.test.js.snap b/src/components/chevronous-text/__tests__/__snapshots__/chevronous-text.test.js.snap
index 8f25dc0..300865c 100644
--- a/src/components/chevronous-text/__tests__/__snapshots__/chevronous-text.test.js.snap
+++ b/src/components/chevronous-text/__tests__/__snapshots__/chevronous-text.test.js.snap
@@ -11,6 +11,8 @@ exports[`ChevronousText additional icon classes renders 1`] = `
     <Icon
       inline={true}
       name="chevron-right"
+      passthroughProps={Object {}}
+      size={18}
       themeIcon="color-blue"
     />
   </span>
@@ -28,6 +30,8 @@ exports[`ChevronousText basic renders 1`] = `
     <Icon
       inline={true}
       name="chevron-right"
+      passthroughProps={Object {}}
+      size={18}
     />
   </span>
 </span>
diff --git a/src/components/code-snippet/__tests__/__snapshots__/code-snippet.test.js.snap b/src/components/code-snippet/__tests__/__snapshots__/code-snippet.test.js.snap
index b4536ea..b716094 100644
--- a/src/components/code-snippet/__tests__/__snapshots__/code-snippet.test.js.snap
+++ b/src/components/code-snippet/__tests__/__snapshots__/code-snippet.test.js.snap
@@ -753,12 +753,8 @@ exports[`CodeSnippet copy ranges, with onCopy callback renders as expected 1`] =
     }
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn--xs py3 px3 round",
-        }
-      }
+      block={false}
+      className="btn btn--xs py3 px3 round"
       onCopy={[Function]}
       text="import Mapbox
 "
@@ -776,12 +772,8 @@ exports[`CodeSnippet copy ranges, with onCopy callback renders as expected 1`] =
     }
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn--xs py3 px3 round",
-        }
-      }
+      block={false}
+      className="btn btn--xs py3 px3 round"
       onCopy={[Function]}
       text="    let url = URL(string: \\"mapbox://styles/mapbox/streets-v9\\")
     let mapView = MGLMapView(frame: view.bounds, styleURL: url)
@@ -1065,12 +1057,8 @@ exports[`CodeSnippet highlighted, no ranges, with onCopy callback renders as exp
     className="absolute z2 top right mr6 mt6 color-white"
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn--xs py3 px3 round",
-        }
-      }
+      block={false}
+      className="btn btn--xs py3 px3 round"
       onCopy={[MockFunction]}
       text="dependencies {
   compile('com.mapbox.mapboxsdk:mapbox-android-sdk:5.0.2@aar') {
diff --git a/src/components/control-checkbox-set/__tests__/__snapshots__/control-checkbox-set.test.js.snap b/src/components/control-checkbox-set/__tests__/__snapshots__/control-checkbox-set.test.js.snap
index 1566f44..2d4af64 100644
--- a/src/components/control-checkbox-set/__tests__/__snapshots__/control-checkbox-set.test.js.snap
+++ b/src/components/control-checkbox-set/__tests__/__snapshots__/control-checkbox-set.test.js.snap
@@ -43,6 +43,8 @@ exports[`ControlCheckbox all options renders 1`] = `
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         <span>
@@ -69,6 +71,8 @@ exports[`ControlCheckbox all options renders 1`] = `
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         <span>
@@ -95,6 +99,8 @@ exports[`ControlCheckbox all options renders 1`] = `
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         <span>
@@ -142,6 +148,8 @@ exports[`ControlCheckbox basic onChange gets called with input id and value 1`]
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         Option 1
@@ -166,6 +174,8 @@ exports[`ControlCheckbox basic onChange gets called with input id and value 1`]
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         Option 2
@@ -211,6 +221,8 @@ exports[`ControlCheckbox basic renders 1`] = `
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         Option 1
@@ -235,6 +247,8 @@ exports[`ControlCheckbox basic renders 1`] = `
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         Option 2
@@ -280,6 +294,8 @@ exports[`ControlCheckbox disabled renders 1`] = `
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         Option 1
@@ -305,6 +321,8 @@ exports[`ControlCheckbox disabled renders 1`] = `
           <Icon
             inline={false}
             name="check"
+            passthroughProps={Object {}}
+            size={18}
           />
         </div>
         Option 2
diff --git a/src/components/control-file/__tests__/__snapshots__/control-file.test.js.snap b/src/components/control-file/__tests__/__snapshots__/control-file.test.js.snap
index ef99948..b0d16a3 100644
--- a/src/components/control-file/__tests__/__snapshots__/control-file.test.js.snap
+++ b/src/components/control-file/__tests__/__snapshots__/control-file.test.js.snap
@@ -36,6 +36,8 @@ exports[`ControlFile all options renders 1`] = `
           <Icon
             inline={false}
             name="harddrive"
+            passthroughProps={Object {}}
+            size={18}
             themeIcon="mr6 inline-block align-middle"
           />
           Select an image
@@ -98,6 +100,8 @@ exports[`ControlFile basic receives an array of one file as value, displaying it
           <Icon
             inline={false}
             name="harddrive"
+            passthroughProps={Object {}}
+            size={18}
             themeIcon="mr6 inline-block align-middle"
           />
           bon jovi.mp3
@@ -125,16 +129,32 @@ exports[`ControlFile basic receives an array of one file as value, displaying it
     <div
       className="flex-child"
     >
-      <IconButton
-        icon="trash"
-        onClick={[Function]}
-        themeButton="link link--gray relative bg-transparent py0 "
-        tooltipProps={
-          Object {
-            "content": "Clear",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Clear"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="block link link--gray relative bg-transparent py0 "
+          data-test="control-file-clear"
+          onClick={[Function]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="trash"
+            passthroughProps={Object {}}
+            size={18}
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </ControlWrapper>
@@ -170,6 +190,8 @@ exports[`ControlFile basic renders 1`] = `
           <Icon
             inline={false}
             name="harddrive"
+            passthroughProps={Object {}}
+            size={18}
             themeIcon="mr6 inline-block align-middle"
           />
           Select a file
@@ -228,6 +250,8 @@ exports[`ControlFile disabled renders 1`] = `
           <Icon
             inline={false}
             name="harddrive"
+            passthroughProps={Object {}}
+            size={18}
             themeIcon="mr6 inline-block align-middle"
           />
           Select a file
@@ -286,6 +310,8 @@ exports[`ControlFile multiple receives an array of files as value, displaying th
           <Icon
             inline={false}
             name="harddrive"
+            passthroughProps={Object {}}
+            size={18}
             themeIcon="mr6 inline-block align-middle"
           />
           bon jovi.mp3, aerosmith.mp3
@@ -314,16 +340,32 @@ exports[`ControlFile multiple receives an array of files as value, displaying th
     <div
       className="flex-child"
     >
-      <IconButton
-        icon="trash"
-        onClick={[Function]}
-        themeButton="link link--gray relative bg-transparent py0 "
-        tooltipProps={
-          Object {
-            "content": "Clear",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Clear"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="block link link--gray relative bg-transparent py0 "
+          data-test="control-file-clear"
+          onClick={[Function]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="trash"
+            passthroughProps={Object {}}
+            size={18}
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </ControlWrapper>
diff --git a/src/components/control-file/__tests__/control-file-test-cases.js b/src/components/control-file/__tests__/control-file-test-cases.js
index 1eabcf5..53e6b20 100644
--- a/src/components/control-file/__tests__/control-file-test-cases.js
+++ b/src/components/control-file/__tests__/control-file-test-cases.js
@@ -4,6 +4,34 @@ import safeSpy from '../../../test-utils/safe-spy';
 
 const testCases = {};
 
+class Interactive extends React.Component {
+  constructor(props) {
+    super(props);
+    this.state = { files: null };
+    this.setFiles = this.setFiles.bind(this);
+  }
+
+  setFiles(files) {
+    this.setState({ files });
+  }
+
+  render() {
+    return (
+      <ControlFile
+        id="interactive-file"
+        label="Select a file"
+        onChange={this.setFiles}
+        value={this.state.files}
+      />
+    );
+  }
+}
+
+testCases.interactive = {
+  description: 'Interactive',
+  element: <Interactive />
+};
+
 testCases.basic = {
   description: 'basic',
   component: ControlFile,
diff --git a/src/components/control-file/__tests__/control-file.test.js b/src/components/control-file/__tests__/control-file.test.js
index dfc9b18..ab771cb 100644
--- a/src/components/control-file/__tests__/control-file.test.js
+++ b/src/components/control-file/__tests__/control-file.test.js
@@ -52,10 +52,7 @@ describe('ControlFile', () => {
       const files = [bonJoviFile];
       wrapper.setProps({ value: files });
       wrapper.update();
-      wrapper
-        .find('IconButton')
-        .props()
-        .onClick();
+      wrapper.find({ 'data-test': 'control-file-clear' }).prop('onClick')();
       wrapper.update();
       expect(testCase.props.onChange).toHaveBeenCalledTimes(1);
       expect(testCase.props.onChange).toHaveBeenCalledWith('', 'testinput-1');
diff --git a/src/components/control-text/__tests__/__snapshots__/control-text.test.js.snap b/src/components/control-text/__tests__/__snapshots__/control-text.test.js.snap
index e073629..e468fee 100644
--- a/src/components/control-text/__tests__/__snapshots__/control-text.test.js.snap
+++ b/src/components/control-text/__tests__/__snapshots__/control-text.test.js.snap
@@ -60,6 +60,8 @@ exports[`ControlText all options renders 1`] = `
         <Icon
           inline={false}
           name="alert"
+          passthroughProps={Object {}}
+          size={18}
           themeIcon="cursor-pointer"
         />
       </span>
@@ -217,6 +219,8 @@ exports[`ControlText error inline option renders 1`] = `
         <Icon
           inline={false}
           name="alert"
+          passthroughProps={Object {}}
+          size={18}
           themeIcon="cursor-pointer"
         />
       </span>
diff --git a/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap b/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
index d33de59..9779836 100644
--- a/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
+++ b/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
@@ -1,171 +1,74 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Copiable all props renders 1`] = `
+exports[`basic renders as expected 1`] = `
 <div
-  className="relative"
+  className="relative clearfix bg-darken5 round"
 >
   <div
-    className="color-gray bg-darken5 round-l txt-fancy py6 px6 txt-s"
-  >
-    <span
-      onClick={[Function]}
-    >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
-  <div
-    className="absolute z2 bottom right mr3 mt3 color-white"
+    className="absolute top right px6 py6"
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn round-r bg-gray-dark px0 py0",
-        }
-      }
-      text="the text you copy the text you copy the text you copy the text you copy the text you copy "
+      block={true}
+      className="btn btn--xs py3 px3 round"
+      text="thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy "
     />
   </div>
-</div>
-`;
-
-exports[`Copiable basic Copy hint disappears at the right time 1`] = `
-<div
-  className="relative round z0 scroll-styled"
->
   <div
-    className="color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s"
+    className="py6 px12"
+    data-test="copiable-text-el"
+    onBlur={[Function]}
+    onFocus={[Function]}
+    tabIndex="-1"
   >
-    <span
-      onClick={[Function]}
-    >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
-  <div
-    className="absolute z2 top right mr3 mt3 color-white"
-  >
-    <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
+    <div
+      className="my3 txt-mono txt-s mr24"
+      style={
         Object {
-          "themeButton": "btn btn--xs py3 px3 round",
+          "overflowWrap": "break-word",
+          "wordWrap": "break-word",
         }
       }
-      text="the text you copy the text you copy the text you copy the text you copy the text you copy "
-    />
-  </div>
-  <Popover
-    accessibleTitle="⌘+C to copy!"
-    alignment="center"
-    allowPlacementAxisChange={true}
-    clickOutsideCloses={true}
-    coloring="light"
-    containWithinViewport={true}
-    escapeCloses={true}
-    getAnchorElement={[Function]}
-    hasPointer={true}
-    hideWhenAnchorIsOffscreen={true}
-    padded={true}
-    placement="top"
-    receiveFocus={true}
-    trapFocus={false}
-  >
-    <div
-      className="txt-s"
     >
-      ⌘+C to copy!
+      thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy 
     </div>
-  </Popover>
-</div>
-`;
-
-exports[`Copiable basic Copy hint disappears at the right time 2`] = `
-<div
-  className="relative round z0 scroll-styled"
->
-  <div
-    className="color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s"
-  >
-    <span
-      onClick={[Function]}
-    >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
-  <div
-    className="absolute z2 top right mr3 mt3 color-white"
-  >
-    <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn btn--xs py3 px3 round",
-        }
-      }
-      text="the text you copy the text you copy the text you copy the text you copy the text you copy "
-    />
   </div>
 </div>
 `;
 
-exports[`Copiable basic renders 1`] = `
+exports[`basic shows copy hint when focused 1`] = `
 <div
-  className="relative round z0 scroll-styled"
+  className="relative clearfix bg-darken5 round"
 >
   <div
-    className="color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s"
-  >
-    <span
-      onClick={[Function]}
-    >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
-  <div
-    className="absolute z2 top right mr3 mt3 color-white"
+    className="absolute top right px6 py6"
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn btn--xs py3 px3 round",
-        }
-      }
-      text="the text you copy the text you copy the text you copy the text you copy the text you copy "
+      block={true}
+      className="btn btn--xs py3 px3 round"
+      text="thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy "
     />
   </div>
-</div>
-`;
-
-exports[`Copiable basic shows copy hint when clicked 1`] = `
-<div
-  className="relative round z0 scroll-styled"
->
-  <div
-    className="color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s"
-  >
-    <span
-      onClick={[Function]}
-    >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
   <div
-    className="absolute z2 top right mr3 mt3 color-white"
+    className="py6 px12"
+    data-test="copiable-text-el"
+    onBlur={[Function]}
+    onFocus={[Function]}
+    tabIndex="-1"
   >
-    <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
+    <div
+      className="my3 txt-mono txt-s mr24"
+      style={
         Object {
-          "themeButton": "btn btn--xs py3 px3 round",
+          "overflowWrap": "break-word",
+          "wordWrap": "break-word",
         }
       }
-      text="the text you copy the text you copy the text you copy the text you copy the text you copy "
-    />
+    >
+      thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy 
+    </div>
   </div>
   <Popover
-    accessibleTitle="⌘+C to copy!"
+    accessibleTitle="Copy the selected text"
     alignment="center"
     allowPlacementAxisChange={true}
     clickOutsideCloses={true}
@@ -175,7 +78,7 @@ exports[`Copiable basic shows copy hint when clicked 1`] = `
     getAnchorElement={[Function]}
     hasPointer={true}
     hideWhenAnchorIsOffscreen={true}
-    padded={true}
+    padding="small"
     placement="top"
     receiveFocus={true}
     trapFocus={false}
@@ -183,7 +86,15 @@ exports[`Copiable basic shows copy hint when clicked 1`] = `
     <div
       className="txt-s"
     >
-      ⌘+C to copy!
+      <span>
+        <span
+          className="txt-kbd"
+        >
+          eh+C
+        </span>
+         
+        to copy
+      </span>
     </div>
   </Popover>
 </div>
diff --git a/src/components/copiable/__tests__/copiable-test-cases.js b/src/components/copiable/__tests__/copiable-test-cases.js
index aaf3cc8..40ba794 100644
--- a/src/components/copiable/__tests__/copiable-test-cases.js
+++ b/src/components/copiable/__tests__/copiable-test-cases.js
@@ -7,21 +7,17 @@ testCases.basic = {
   component: Copiable,
   props: {
     value:
-      'the text you copy the text you copy the text you copy the text you copy the text you copy '
+      'thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy '
   }
 };
 
-testCases.allProps = {
-  description: 'all props',
+testCases.truncated = {
+  description: 'truncated',
   component: Copiable,
   props: {
+    truncated: true,
     value:
-      'the text you copy the text you copy the text you copy the text you copy the text you copy ',
-    themeWrapper: 'relative',
-    themeCopyButtonContainer: 'absolute z2 bottom right mr3 mt3 color-white',
-    themeCopyButton: 'btn round-r bg-gray-dark px0 py0',
-    themeText: 'color-gray bg-darken5 round-l txt-fancy py6 px6 txt-s',
-    themeTooltip: 'round py6 px12 shadow-darken25 bg-blue'
+      'the text you copy the text you copy the text you copy the text you copy the text you copy '
   }
 };
 
diff --git a/src/components/copiable/__tests__/copiable.test.js b/src/components/copiable/__tests__/copiable.test.js
index 39274e3..c80e24d 100644
--- a/src/components/copiable/__tests__/copiable.test.js
+++ b/src/components/copiable/__tests__/copiable.test.js
@@ -1,106 +1,75 @@
 import React from 'react';
 import { shallow } from 'enzyme';
 import delay from 'delay';
+import CopyButton from '../../copy-button';
 import { testCases } from './copiable-test-cases';
-import getWindow from '../../utils/get-window';
 
 const select = require('select');
 
 const FEEDBACK_TIME = 2000;
-const mockUserAgent =
-  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36';
 const mockEvent = {
   preventDefault: jest.fn()
 };
 
-jest.mock('select');
-jest.mock('clipboard/dist/clipboard.min.js', () => {
-  class Clipboard {}
-  Clipboard.isSupported = () => true;
-  return Clipboard;
-});
-jest.mock('../../utils/get-window', () => {
-  return jest.fn();
-});
-select.mockImplementation(() => jest.fn());
+jest.mock('select', () => jest.fn());
+jest.mock('os-key', () =>
+  jest.fn(() => ({ primaryMeta: true, meta: { symbol: 'eh' } }))
+);
+jest.mock('../../utils/get-window', () =>
+  jest.fn(() => ({
+    navigator: {
+      userAgent: 'mock-user-agent'
+    }
+  }))
+);
+jest.spyOn(CopyButton, 'isCopySupported').mockImplementation(() => true);
 
-describe('Copiable', () => {
-  let testCase;
-  let wrapper;
+let testCase;
+let wrapper;
 
+describe(testCases.basic.description, () => {
   beforeEach(() => {
-    getWindow.mockReturnValue({
-      navigator: {
-        userAgent: mockUserAgent
-      }
-    });
+    testCase = testCases.basic;
+    wrapper = shallow(React.createElement(testCase.component, testCase.props));
   });
 
-  describe(testCases.basic.description, () => {
-    beforeEach(() => {
-      testCase = testCases.basic;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-    });
+  test('renders as expected', () => {
+    expect(wrapper).toMatchSnapshot();
+  });
 
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-    });
+  test('calls select library with element holding text to copy', () => {
+    const mockSelectableEl = <span>text to copy</span>;
+    wrapper.instance().getTextEl = jest.fn(() => mockSelectableEl);
+    expect(wrapper.find('Popover').length).toBe(0);
+    wrapper.find({ 'data-test': 'copiable-text-el' }).prop('onFocus')(
+      mockEvent
+    );
+    wrapper.update();
+    expect(select).toHaveBeenCalledTimes(1);
+    expect(wrapper.instance().getTextEl).toHaveBeenCalledTimes(1);
+    expect(select).toHaveBeenCalledWith(mockSelectableEl);
+  });
 
-    test('calls select library with element holding text to copy', () => {
-      const mockSelectableEl = <span>text to copy</span>;
-      wrapper.instance().getTextEl = jest.fn(() => mockSelectableEl);
-      expect(wrapper.find('Popover').length).toBe(0);
-      wrapper
-        .find('span')
-        .props()
-        .onClick(mockEvent);
-      wrapper.update();
-      expect(select).toHaveBeenCalledTimes(1);
-      expect(wrapper.instance().getTextEl).toHaveBeenCalledTimes(1);
-      expect(select).toHaveBeenCalledWith(mockSelectableEl);
-    });
+  test('shows copy hint when focused', () => {
+    expect(wrapper.find('Popover').length).toBe(0);
+    wrapper.find({ 'data-test': 'copiable-text-el' }).prop('onFocus')(
+      mockEvent
+    );
+    wrapper.update();
+    expect(wrapper.find('Popover').length).toBe(1);
+    expect(wrapper).toMatchSnapshot();
+  });
 
-    test('shows copy hint when clicked', () => {
-      expect(wrapper.find('Popover').length).toBe(0);
-      wrapper
-        .find('span')
-        .props()
-        .onClick(mockEvent);
+  test('copy hint disappears at the right time', () => {
+    expect(wrapper.find('Popover').length).toBe(0);
+    wrapper.find({ 'data-test': 'copiable-text-el' }).prop('onFocus')(
+      mockEvent
+    );
+    wrapper.update();
+    expect(wrapper.find('Popover').length).toBe(1);
+    return delay(FEEDBACK_TIME + 100).then(() => {
       wrapper.update();
-      expect(wrapper.find('Popover').length).toBe(1);
-      expect(wrapper).toMatchSnapshot();
-    });
-
-    test('Copy hint disappears at the right time', () => {
       expect(wrapper.find('Popover').length).toBe(0);
-      wrapper
-        .find('span')
-        .props()
-        .onClick(mockEvent);
-      wrapper.update();
-      expect(wrapper).toMatchSnapshot();
-      expect(wrapper.find('Popover').length).toBe(1);
-      return delay(FEEDBACK_TIME + 100).then(() => {
-        wrapper.update();
-        expect(wrapper.find('Popover').length).toBe(0);
-        expect(wrapper).toMatchSnapshot();
-      });
-    });
-  });
-
-  describe(testCases.allProps.description, () => {
-    beforeEach(() => {
-      testCase = testCases.allProps;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-      wrapper.instance().selectText = jest.fn();
-    });
-
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
     });
   });
 });
diff --git a/src/components/copy-button/__tests__/__snapshots__/copy-button.test.js.snap b/src/components/copy-button/__tests__/__snapshots__/copy-button.test.js.snap
index 78f9220..ecfdd9e 100644
--- a/src/components/copy-button/__tests__/__snapshots__/copy-button.test.js.snap
+++ b/src/components/copy-button/__tests__/__snapshots__/copy-button.test.js.snap
@@ -1,17 +1,29 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`CopyButton all props changes state on click 1`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="more copiable text"
+exports[`CopyButton Defaults changes state on click 1`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={true}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
   <button
-    className="btn px12 py12 btn--red"
+    className="btn btn--xs py3 px3 round"
+    data-clipboard-text="Copied by the default test case"
+    onClick={[Function]}
     type="button"
   >
     <Icon
       inline={false}
       name="check"
+      passthroughProps={Object {}}
+      size={18}
     />
     <Popover
       accessibleTitle={null}
@@ -24,94 +36,104 @@ exports[`CopyButton all props changes state on click 1`] = `
       getAnchorElement={[Function]}
       hasPointer={true}
       hideWhenAnchorIsOffscreen={true}
-      padded={true}
+      padding="small"
       placement="top"
       receiveFocus={true}
       trapFocus={false}
     >
-      <span
-        className="block txt-s"
+      <div
+        className="txt-s"
       >
         Copied!
-      </span>
+      </div>
     </Popover>
   </button>
-</div>
+</Tooltip>
 `;
 
-exports[`CopyButton all props changes state on click 2`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="more copiable text"
+exports[`CopyButton Defaults changes state on click 2`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={false}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
   <button
-    className="btn px12 py12 btn--red"
+    className="btn btn--xs py3 px3 round"
+    data-clipboard-text="Copied by the default test case"
+    onClick={[Function]}
     type="button"
   >
     <Icon
       inline={false}
-      name="check"
+      name="clipboard"
+      passthroughProps={Object {}}
+      size={18}
     />
-    <Popover
-      accessibleTitle={null}
-      alignment="center"
-      allowPlacementAxisChange={true}
-      clickOutsideCloses={true}
-      coloring="light"
-      containWithinViewport={true}
-      escapeCloses={true}
-      getAnchorElement={[Function]}
-      hasPointer={true}
-      hideWhenAnchorIsOffscreen={true}
-      padded={true}
-      placement="top"
-      receiveFocus={true}
-      trapFocus={false}
-    >
-      <span
-        className="block txt-s"
-      >
-        Copied!
-      </span>
-    </Popover>
   </button>
-</div>
+</Tooltip>
 `;
 
-exports[`CopyButton all props renders 1`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="more copiable text"
+exports[`CopyButton Defaults renders as expected 1`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={false}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
-  <IconButton
-    data-test="copy-button"
-    icon="clipboard"
+  <button
+    className="btn btn--xs py3 px3 round"
+    data-clipboard-text="Copied by the default test case"
     onClick={[Function]}
-    themeButton="px12 py12 btn--red"
-    themeIcon="color-yellow"
-    tooltipProps={
-      Object {
-        "content": "Copy",
-        "placement": "bottom",
-        "themeTooltip": "color-gray shadow-darken50",
-      }
-    }
-  />
-</div>
+    type="button"
+  >
+    <Icon
+      inline={false}
+      name="clipboard"
+      passthroughProps={Object {}}
+      size={18}
+    />
+  </button>
+</Tooltip>
 `;
 
-exports[`CopyButton basic changes state on click 1`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="the text you copy"
+exports[`CopyButton all props changes state on click 1`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={true}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
   <button
-    className="btn btn--xs py3 px3 round"
+    className="px6 py6 btn btn--purple btn--stroke"
+    data-clipboard-text="more copiable text"
+    data-test="copy-button"
+    onClick={[Function]}
     type="button"
   >
     <Icon
       inline={false}
       name="check"
+      passthroughProps={Object {}}
+      size={18}
     />
     <Popover
       accessibleTitle={null}
@@ -124,53 +146,77 @@ exports[`CopyButton basic changes state on click 1`] = `
       getAnchorElement={[Function]}
       hasPointer={true}
       hideWhenAnchorIsOffscreen={true}
-      padded={true}
+      padding="small"
       placement="top"
       receiveFocus={true}
       trapFocus={false}
     >
-      <span
-        className="block txt-s"
+      <div
+        className="txt-s"
       >
         Copied!
-      </span>
+      </div>
     </Popover>
   </button>
-</div>
+</Tooltip>
 `;
 
-exports[`CopyButton basic changes state on click 2`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="the text you copy"
+exports[`CopyButton all props changes state on click 2`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={false}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
-  <IconButton
-    icon="clipboard"
+  <button
+    className="px6 py6 btn btn--purple btn--stroke"
+    data-clipboard-text="more copiable text"
+    data-test="copy-button"
     onClick={[Function]}
-    themeButton="btn--xs py3 px3 round"
-    tooltipProps={
-      Object {
-        "content": "Copy",
-      }
-    }
-  />
-</div>
+    type="button"
+  >
+    <Icon
+      inline={false}
+      name="clipboard"
+      passthroughProps={Object {}}
+      size={18}
+    />
+  </button>
+</Tooltip>
 `;
 
-exports[`CopyButton basic renders 1`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="the text you copy"
+exports[`CopyButton all props renders as expected 1`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={false}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
-  <IconButton
-    icon="clipboard"
+  <button
+    className="px6 py6 btn btn--purple btn--stroke"
+    data-clipboard-text="more copiable text"
+    data-test="copy-button"
     onClick={[Function]}
-    themeButton="btn--xs py3 px3 round"
-    tooltipProps={
-      Object {
-        "content": "Copy",
-      }
-    }
-  />
-</div>
+    type="button"
+  >
+    <Icon
+      inline={false}
+      name="clipboard"
+      passthroughProps={Object {}}
+      size={18}
+    />
+  </button>
+</Tooltip>
 `;
diff --git a/src/components/copy-button/__tests__/copy-button-test-cases.js b/src/components/copy-button/__tests__/copy-button-test-cases.js
index abeba50..c84f000 100644
--- a/src/components/copy-button/__tests__/copy-button-test-cases.js
+++ b/src/components/copy-button/__tests__/copy-button-test-cases.js
@@ -5,11 +5,11 @@ import safeSpy from '../../../test-utils/safe-spy';
 
 const testCases = {};
 
-testCases.basic = {
-  description: 'basic',
+testCases.defaults = {
+  description: 'Defaults',
   component: CopyButton,
   props: {
-    text: 'the text you copy'
+    text: 'Copied by the default test case'
   }
 };
 
@@ -19,15 +19,9 @@ testCases.allProps = {
   props: {
     text: 'more copiable text',
     onCopy: safeSpy(),
-    feedbackTime: 1000,
-    iconButtonProps: {
-      tooltipProps: {
-        themeTooltip: 'color-gray shadow-darken50',
-        placement: 'bottom'
-      },
-      themeButton: 'px12 py12 btn--red',
-      'data-test': 'copy-button',
-      themeIcon: 'color-yellow'
+    className: 'px6 py6 btn btn--purple btn--stroke',
+    passthroughProps: {
+      'data-test': 'copy-button'
     }
   }
 };
@@ -46,8 +40,8 @@ class InModal extends React.Component {
       return null;
     }
     return (
-      <Modal title="Copy test" onExit={this.toggleModal}>
-        <CopyButton text="This is the text you're copying" />
+      <Modal accessibleTitle="Copy test" onExit={this.toggleModal}>
+        <CopyButton text="You copied this text from a modal" />
       </Modal>
     );
   }
diff --git a/src/components/copy-button/__tests__/copy-button.test.js b/src/components/copy-button/__tests__/copy-button.test.js
index 3da9a66..154c8a6 100644
--- a/src/components/copy-button/__tests__/copy-button.test.js
+++ b/src/components/copy-button/__tests__/copy-button.test.js
@@ -28,23 +28,20 @@ describe('CopyButton', () => {
     });
   });
 
-  describe(testCases.basic.description, () => {
+  describe(testCases.defaults.description, () => {
     beforeEach(() => {
-      testCase = testCases.basic;
+      testCase = testCases.defaults;
       wrapper = shallow(
         React.createElement(testCase.component, testCase.props)
       );
     });
 
-    test('renders', () => {
+    test('renders as expected', () => {
       expect(wrapper).toMatchSnapshot();
     });
 
     test('changes state on click', () => {
-      wrapper
-        .find('IconButton')
-        .props()
-        .onClick();
+      wrapper.find('button').prop('onClick')();
       wrapper.update();
       expect(wrapper).toMatchSnapshot();
       return delay(820).then(() => {
@@ -62,16 +59,13 @@ describe('CopyButton', () => {
       );
     });
 
-    test('renders', () => {
+    test('renders as expected', () => {
       expect(wrapper).toMatchSnapshot();
     });
 
     // Extra classes need to carry over to both states
     test('changes state on click', () => {
-      wrapper
-        .find('IconButton')
-        .props()
-        .onClick();
+      wrapper.find('button').prop('onClick')();
       wrapper.update();
       expect(wrapper).toMatchSnapshot();
       return delay(820).then(() => {
@@ -81,10 +75,7 @@ describe('CopyButton', () => {
     });
 
     test('calls onCopy callback', () => {
-      wrapper
-        .find('IconButton')
-        .props()
-        .onClick();
+      wrapper.find('button').prop('onClick')();
       expect(testCase.props.onCopy).toHaveBeenCalledTimes(1);
       expect(testCase.props.onCopy).toHaveBeenCalledWith(testCase.props.text);
     });
diff --git a/src/components/icon-button/__tests__/__snapshots__/icon-button.test.js.snap b/src/components/icon-button/__tests__/__snapshots__/icon-button.test.js.snap
deleted file mode 100644
index 213ff7f..0000000
--- a/src/components/icon-button/__tests__/__snapshots__/icon-button.test.js.snap
+++ /dev/null
@@ -1,68 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`IconButton all props used, JSX content renders 1`] = `
-<Tooltip
-  alignment="right"
-  backgroundColor="yellow"
-  coloring="light"
-  content={
-    <span
-      className="txt-bold"
-    >
-      tada
-    </span>
-  }
-  disabled={false}
-  display="inline-block"
-  maxWidth="medium"
-  padded={true}
-  placement="right"
-  respondsToClick={true}
-  testId="tooltip-test-id"
-  textSize="s"
-  themeTooltip="color-gray round px12 py12 shadow-darken25"
-/>
-`;
-
-exports[`IconButton all props used, JSX content renders 2`] = `
-<button
-  className="btn px12 py12 bg-gray"
-  data-test="button-test-id"
-  onClick={[MockFunction]}
-  type="button"
->
-  <Icon
-    inline={false}
-    name="bike"
-    themeIcon="color-gray-dark"
-  />
-</button>
-`;
-
-exports[`IconButton basic renders 1`] = `
-<Tooltip
-  alignment="center"
-  coloring="light"
-  content="close"
-  disabled={false}
-  display="inline-block"
-  maxWidth="medium"
-  padded={true}
-  placement="top"
-  respondsToClick={false}
-  textSize="s"
-/>
-`;
-
-exports[`IconButton basic renders 2`] = `
-<button
-  className="btn btn--xs px0"
-  onClick={[MockFunction]}
-  type="button"
->
-  <Icon
-    inline={false}
-    name="close"
-  />
-</button>
-`;
diff --git a/src/components/icon-button/__tests__/icon-button-test-cases.js b/src/components/icon-button/__tests__/icon-button-test-cases.js
deleted file mode 100644
index 8477e8b..0000000
--- a/src/components/icon-button/__tests__/icon-button-test-cases.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import safeSpy from '../../../test-utils/safe-spy';
-import IconButton from '../icon-button';
-
-const testCases = {};
-
-testCases.basic = {
-  description: 'basic',
-  component: IconButton,
-  props: {
-    onClick: safeSpy(),
-    icon: 'close',
-    tooltipProps: {
-      content: 'close'
-    }
-  }
-};
-
-testCases.allPropsJsxContent = {
-  description: 'all props used, JSX content',
-  component: IconButton,
-  props: {
-    onClick: safeSpy(),
-    icon: 'bike',
-    tooltipProps: {
-      content: <span className="txt-bold">tada</span>,
-      placement: 'right',
-      alignment: 'right',
-      disabled: false,
-      backgroundColor: 'yellow',
-      respondsToClick: true,
-      themeTooltip: 'color-gray round px12 py12 shadow-darken25',
-      display: 'inline-block',
-      testId: 'tooltip-test-id'
-    },
-    themeButton: 'px12 py12 bg-gray',
-    themeIcon: 'color-gray-dark',
-    'data-test': 'button-test-id'
-  }
-};
-
-export { testCases };
diff --git a/src/components/icon-button/__tests__/icon-button.test.js b/src/components/icon-button/__tests__/icon-button.test.js
deleted file mode 100644
index dfb5542..0000000
--- a/src/components/icon-button/__tests__/icon-button.test.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import { testCases } from './icon-button-test-cases';
-
-describe('IconButton', () => {
-  let testCase;
-  let wrapper;
-  let body;
-
-  describe(testCases.basic.description, () => {
-    beforeEach(() => {
-      testCase = testCases.basic;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-      body = shallow(
-        wrapper
-          .find('Tooltip')
-          .props()
-          .children()
-      );
-    });
-
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-      expect(body).toMatchSnapshot();
-    });
-
-    test('button responds to click', () => {
-      body.props().onClick();
-      expect(testCase.props.onClick).toHaveBeenCalledTimes(1);
-    });
-  });
-
-  describe(testCases.allPropsJsxContent.description, () => {
-    beforeEach(() => {
-      testCase = testCases.allPropsJsxContent;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-      body = shallow(
-        wrapper
-          .find('Tooltip')
-          .props()
-          .children()
-      );
-    });
-
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-      expect(body).toMatchSnapshot();
-    });
-  });
-});
diff --git a/src/components/icon-text/__tests__/__snapshots__/icon-text.test.js.snap b/src/components/icon-text/__tests__/__snapshots__/icon-text.test.js.snap
index fcfad5f..3e205ea 100644
--- a/src/components/icon-text/__tests__/__snapshots__/icon-text.test.js.snap
+++ b/src/components/icon-text/__tests__/__snapshots__/icon-text.test.js.snap
@@ -15,6 +15,8 @@ exports[`icon after renders as expected 1`] = `
     <Icon
       inline={false}
       name="chevron-right"
+      passthroughProps={Object {}}
+      size={18}
     />
   </span>
 </span>
@@ -30,6 +32,8 @@ exports[`icon before renders as expected 1`] = `
     <Icon
       inline={false}
       name="clipboard"
+      passthroughProps={Object {}}
+      size={18}
     />
   </span>
   <span
@@ -50,6 +54,8 @@ exports[`icons on both sides, including a non-string one renders as expected 1`]
     <Icon
       inline={false}
       name="check"
+      passthroughProps={Object {}}
+      size={18}
     />
   </span>
   <span
@@ -63,6 +69,8 @@ exports[`icons on both sides, including a non-string one renders as expected 1`]
     <Icon
       inline={false}
       name="github"
+      passthroughProps={Object {}}
+      size={18}
     />
   </span>
 </span>
@@ -78,6 +86,8 @@ exports[`large spacing renders as expected 1`] = `
     <Icon
       inline={false}
       name="check"
+      passthroughProps={Object {}}
+      size={18}
     />
   </span>
   <span
diff --git a/src/components/icon/__tests__/__snapshots__/icon.test.js.snap b/src/components/icon/__tests__/__snapshots__/icon.test.js.snap
index 75bac8f..a5a72c1 100644
--- a/src/components/icon/__tests__/__snapshots__/icon.test.js.snap
+++ b/src/components/icon/__tests__/__snapshots__/icon.test.js.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Icon basic renders 1`] = `
+exports[`basic renders as expected 1`] = `
 <svg
   className="events-none icon"
   focusable="false"
@@ -19,9 +19,9 @@ exports[`Icon basic renders 1`] = `
 </svg>
 `;
 
-exports[`Icon extra props renders 1`] = `
+exports[`extra props renders as expected 1`] = `
 <svg
-  className="events-none icon color-red"
+  className="events-none icon"
   data-test="alert-icon"
   focusable="false"
   role="presentation"
@@ -39,9 +39,9 @@ exports[`Icon extra props renders 1`] = `
 </svg>
 `;
 
-exports[`Icon inline renders 1`] = `
+exports[`inline renders as expected 1`] = `
 <svg
-  className="events-none icon mr3 color-green inline-block align-t"
+  className="events-none icon inline-block align-t"
   focusable="false"
   role="presentation"
   style={
@@ -58,16 +58,16 @@ exports[`Icon inline renders 1`] = `
 </svg>
 `;
 
-exports[`Icon special size with classes renders 1`] = `
+exports[`special size applied with a style renders as expected 1`] = `
 <svg
-  className="events-none icon h36 w36"
+  className="events-none icon"
   data-test="alert-icon"
   focusable="false"
   role="presentation"
   style={
     Object {
-      "height": 18,
-      "width": 18,
+      "height": 36,
+      "width": 36,
     }
   }
 >
@@ -78,10 +78,9 @@ exports[`Icon special size with classes renders 1`] = `
 </svg>
 `;
 
-exports[`Icon special size with styles renders 1`] = `
+exports[`special size renders as expected 1`] = `
 <svg
   className="events-none icon"
-  data-test="alert-icon"
   focusable="false"
   role="presentation"
   style={
diff --git a/src/components/icon/__tests__/icon-test-cases.js b/src/components/icon/__tests__/icon-test-cases.js
index 65d2590..f463efb 100644
--- a/src/components/icon/__tests__/icon-test-cases.js
+++ b/src/components/icon/__tests__/icon-test-cases.js
@@ -16,39 +16,49 @@ testCases.extraProps = {
   component: Icon,
   props: {
     name: 'alert',
-    'data-test': 'alert-icon',
-    themeIcon: 'color-red'
+    passthroughProps: {
+      'data-test': 'alert-icon'
+    }
   }
 };
 
-testCases.specialSizeClasses = {
-  description: 'special size with classes',
+testCases.specialSize = {
+  description: 'special size',
   component: Icon,
   props: {
     name: 'alert',
-    'data-test': 'alert-icon',
-    themeIcon: 'h36 w36'
+    size: 36
   }
 };
 
 testCases.specialSizeStyle = {
-  description: 'special size with styles',
+  description: 'special size applied with a style',
   component: Icon,
   props: {
     name: 'alert',
-    'data-test': 'alert-icon',
-    style: { height: 36, width: 36 }
+    passthroughProps: {
+      'data-test': 'alert-icon',
+      style: { height: 36, width: 36 }
+    }
   }
 };
 
 testCases.inline = {
-  noDisplay: true,
   description: 'inline',
   component: Icon,
+  props: {
+    name: 'check',
+    inline: true
+  }
+};
+
+testCases.inlineWithSize = {
+  description: 'inline with size',
+  component: Icon,
   props: {
     name: 'check',
     inline: true,
-    themeIcon: 'mr3 color-green'
+    size: 45
   }
 };
 
@@ -56,7 +66,21 @@ testCases.inlineSmallDisplay = {
   description: 'inline txt-s',
   element: (
     <div className="txt-s">
-      <Icon name="check" themeIcon="mr3 color-green" inline={true} />
+      <span className="mr3 color-green">
+        <Icon name="check" inline={true} />
+      </span>
+      You did it
+    </div>
+  )
+};
+
+testCases.inlineLargeText = {
+  description: 'inline txt-xl',
+  element: (
+    <div className="txt-xl">
+      <span className="mr3 color-green">
+        <Icon name="check" inline={true} />
+      </span>
       You did it
     </div>
   )
@@ -66,24 +90,21 @@ testCases.inlineLargeDisplay = {
   description: 'inline txt-xl',
   element: (
     <div className="txt-xl">
-      <Icon name="check" themeIcon="mr3 color-green" inline={true} />
+      <span className="mr3 color-green">
+        <Icon name="check" inline={true} size={45} />
+      </span>
       You did it
     </div>
   )
 };
 
-testCases.inlineHeadingDisplay = {
-  description: 'inline txt-h2',
+testCases.inlineMultiline = {
+  description: 'inline txt-xl',
   element: (
-    <div className="txt-h2">
-      Party because
-      <Icon
-        name="check"
-        themeIcon="mr3 color-green"
-        inline={true}
-        style={{ width: '1em' }}
-      />
-      you did it
+    <div>
+      The last thing you want in your kitchen is a{' '}
+      <Icon name="flame" inline={true} /> fire even though there is{' '}
+      <Icon name="water" inline={true} /> water nearby.
     </div>
   )
 };
diff --git a/src/components/icon/__tests__/icon.test.js b/src/components/icon/__tests__/icon.test.js
index 3538dcf..6ffe176 100644
--- a/src/components/icon/__tests__/icon.test.js
+++ b/src/components/icon/__tests__/icon.test.js
@@ -7,85 +7,85 @@ jest.mock('../../utils/get-window', () => {
   return jest.fn();
 });
 
-describe('Icon', () => {
-  let testCase;
-  let wrapper;
+let testCase;
+let wrapper;
 
-  describe(testCases.basic.description, () => {
-    beforeEach(() => {
-      testCase = testCases.basic;
-    });
+describe(testCases.basic.description, () => {
+  beforeEach(() => {
+    testCase = testCases.basic;
+  });
+
+  test('renders as expected', () => {
+    const wrapper = shallow(
+      React.createElement(testCase.component, testCase.props)
+    );
+    expect(wrapper).toMatchSnapshot();
+  });
+});
 
-    test('renders', () => {
-      const wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-      expect(wrapper).toMatchSnapshot();
-    });
+describe(testCases.extraProps.description, () => {
+  beforeEach(() => {
+    testCase = testCases.extraProps;
+    wrapper = shallow(React.createElement(testCase.component, testCase.props));
   });
 
-  describe(testCases.extraProps.description, () => {
-    beforeEach(() => {
-      testCase = testCases.extraProps;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-    });
+  test('renders as expected', () => {
+    expect(wrapper).toMatchSnapshot();
+  });
+});
 
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-    });
+describe(testCases.specialSize.description, () => {
+  beforeEach(() => {
+    testCase = testCases.specialSize;
+    wrapper = shallow(React.createElement(testCase.component, testCase.props));
   });
 
-  describe(testCases.specialSizeClasses.description, () => {
-    beforeEach(() => {
-      testCase = testCases.specialSizeClasses;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-    });
+  test('renders as expected', () => {
+    expect(wrapper).toMatchSnapshot();
+  });
+});
 
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-    });
+describe(testCases.specialSizeStyle.description, () => {
+  beforeEach(() => {
+    testCase = testCases.specialSizeStyle;
+    wrapper = shallow(React.createElement(testCase.component, testCase.props));
   });
 
-  describe(testCases.specialSizeStyle.description, () => {
-    beforeEach(() => {
-      testCase = testCases.specialSizeStyle;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-    });
+  test('renders as expected', () => {
+    expect(wrapper).toMatchSnapshot();
+  });
+});
 
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-    });
+describe(testCases.inline.description, () => {
+  beforeEach(() => {
+    testCase = testCases.inline;
+    const mockWindow = {
+      getComputedStyle: jest.fn(() => ({
+        'line-height': '14px'
+      }))
+    };
+    getWindow.mockReturnValueOnce(mockWindow);
   });
 
-  describe(testCases.inline.description, () => {
-    beforeEach(() => {
-      testCase = testCases.inline;
-      const mockWindow = {
-        getComputedStyle: jest.fn(() => ({
-          'line-height': '14px'
-        }))
-      };
-      getWindow.mockReturnValueOnce(mockWindow);
-    });
+  test('renders as expected', () => {
+    const wrapper = shallow(
+      React.createElement(testCase.component, testCase.props)
+    );
+    expect(wrapper).toMatchSnapshot();
+  });
 
-    test('renders', () => {
-      const wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-      expect(wrapper).toMatchSnapshot();
-    });
+  test('after mount, adjusts height to match line-height', () => {
+    const wrapper = mount(
+      React.createElement(testCase.component, testCase.props)
+    );
+    expect(wrapper.instance().iconElement.style.height).toBe('14px');
+  });
 
-    test('after mount, adjusts height to match line-height', () => {
-      const wrapper = mount(
-        React.createElement(testCase.component, testCase.props)
-      );
-      expect(wrapper.instance().iconElement.style.height).toBe('14px');
-    });
+  test('after update, adjusts height to match line-height', () => {
+    const wrapper = mount(
+      React.createElement(testCase.component, testCase.props)
+    );
+    wrapper.setProps({ size: 45 });
+    expect(wrapper.instance().iconElement.style.height).toBe('14px');
   });
 });
diff --git a/src/components/modal/__tests__/__snapshots__/modal.test.js.snap b/src/components/modal/__tests__/__snapshots__/modal.test.js.snap
index a752129..56f8edc 100644
--- a/src/components/modal/__tests__/__snapshots__/modal.test.js.snap
+++ b/src/components/modal/__tests__/__snapshots__/modal.test.js.snap
@@ -28,16 +28,32 @@ exports[`Modal No fixed width, display only renders 1`] = `
     <div
       className="absolute top right"
     >
-      <IconButton
-        icon="close"
-        onClick={[MockFunction]}
-        themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-        tooltipProps={
-          Object {
-            "content": "Close",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Close"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+          data-test="modal-close"
+          onClick={[MockFunction]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="close"
+            passthroughProps={Object {}}
+            size={18}
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </Displaced>
@@ -63,7 +79,7 @@ exports[`Modal all options renders 1`] = `
   }
 >
   <div
-    className="relative wmax-full w360 bg-white round"
+    className="relative wmax-full w360 bg-white round px36 py36"
   >
     <div>
       <div
@@ -81,16 +97,32 @@ exports[`Modal all options renders 1`] = `
     <div
       className="absolute top right"
     >
-      <IconButton
-        icon="close"
-        onClick={[MockFunction]}
-        themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-        tooltipProps={
-          Object {
-            "content": "Close",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Close"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+          data-test="modal-close"
+          onClick={[MockFunction]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="close"
+            passthroughProps={Object {}}
+            size={18}
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </Displaced>
@@ -149,16 +181,32 @@ exports[`Modal basic default renders 1`] = `
     <div
       className="absolute top right"
     >
-      <IconButton
-        icon="close"
-        onClick={[MockFunction]}
-        themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-        tooltipProps={
-          Object {
-            "content": "Close",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Close"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+          data-test="modal-close"
+          onClick={[MockFunction]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="close"
+            passthroughProps={Object {}}
+            size={18}
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </Displaced>
@@ -204,16 +252,32 @@ exports[`Modal basic small renders 1`] = `
     <div
       className="absolute top right"
     >
-      <IconButton
-        icon="close"
-        onClick={[MockFunction]}
-        themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-        tooltipProps={
-          Object {
-            "content": "Close",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Close"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+          data-test="modal-close"
+          onClick={[MockFunction]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="close"
+            passthroughProps={Object {}}
+            size={18}
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </Displaced>
diff --git a/src/components/modal/__tests__/modal-test-cases.js b/src/components/modal/__tests__/modal-test-cases.js
index a4c76c5..2051600 100644
--- a/src/components/modal/__tests__/modal-test-cases.js
+++ b/src/components/modal/__tests__/modal-test-cases.js
@@ -107,7 +107,7 @@ testCases.unpadded = {
             <div className="flex-child px24 py24">The other side</div>
           </div>
         ),
-        padded: false
+        padding: 'none'
       }}
     />
   )
diff --git a/src/components/modal/__tests__/modal.test.js b/src/components/modal/__tests__/modal.test.js
index f482510..39b5312 100644
--- a/src/components/modal/__tests__/modal.test.js
+++ b/src/components/modal/__tests__/modal.test.js
@@ -20,7 +20,7 @@ describe('Modal', () => {
 
     test('fires onExit', () => {
       // find the button that triggers onExit method
-      wrapper.find('IconButton').simulate('click');
+      wrapper.find({ 'data-test': 'modal-close' }).prop('onClick')();
 
       expect(testCase.props.onExit).toHaveBeenCalledTimes(1);
     });
@@ -40,7 +40,7 @@ describe('Modal', () => {
 
     test('fires onExit', () => {
       // find the button that triggers onExit method
-      wrapper.find('IconButton').simulate('click');
+      wrapper.find({ 'data-test': 'modal-close' }).prop('onClick')();
 
       expect(testCase.props.onExit).toHaveBeenCalledTimes(1);
     });
@@ -60,7 +60,7 @@ describe('Modal', () => {
 
     test('fires onExit', () => {
       // find the button that triggers onExit method
-      wrapper.find('IconButton').simulate('click');
+      wrapper.find({ 'data-test': 'modal-close' }).prop('onClick')();
 
       expect(testCase.props.onExit).toHaveBeenCalledTimes(1);
     });
@@ -91,8 +91,8 @@ describe('Modal', () => {
       expect(wrapper).toMatchSnapshot();
     });
 
-    test('IconButton is not present', () => {
-      expect(wrapper.find('IconButton').exists()).toBe(false);
+    test('close button is not present', () => {
+      expect(wrapper.find({ 'data-test': 'modal-close' }).exists()).toBe(false);
     });
   });
 });
diff --git a/src/components/popover-trigger/__tests__/__snapshots__/popover-trigger.test.js.snap b/src/components/popover-trigger/__tests__/__snapshots__/popover-trigger.test.js.snap
index dce77b7..62c366a 100644
--- a/src/components/popover-trigger/__tests__/__snapshots__/popover-trigger.test.js.snap
+++ b/src/components/popover-trigger/__tests__/__snapshots__/popover-trigger.test.js.snap
@@ -2,15 +2,11 @@
 
 exports[`PopoverTrigger default props renders 1`] = `
 <div
+  className="inline-block"
   onBlur={[Function]}
   onClick={[Function]}
   onFocus={[Function]}
   onKeyDown={[Function]}
-  style={
-    Object {
-      "display": "inline-block",
-    }
-  }
 >
   <button
     className="btn"
@@ -22,15 +18,11 @@ exports[`PopoverTrigger default props renders 1`] = `
 
 exports[`PopoverTrigger responds to all interactions renders 1`] = `
 <div
+  className="inline-block"
   onBlur={[Function]}
   onClick={[Function]}
   onFocus={[Function]}
   onKeyDown={[Function]}
-  style={
-    Object {
-      "display": "inline-block",
-    }
-  }
 >
   <button
     className="btn"
diff --git a/src/components/popover/__tests__/__snapshots__/popover.test.js.snap b/src/components/popover/__tests__/__snapshots__/popover.test.js.snap
index b72edac..7f0470c 100644
--- a/src/components/popover/__tests__/__snapshots__/popover.test.js.snap
+++ b/src/components/popover/__tests__/__snapshots__/popover.test.js.snap
@@ -15,7 +15,7 @@ exports[`Popover all props renders 1`] = `
   pointerColor="#fff"
 >
   <div
-    className="color-text shadow-darken25 round px12 py12"
+    className="color-text shadow-darken25 round"
     data-popover-id={4}
     data-test={true}
     key="body"
diff --git a/src/components/popover/__tests__/popover-test-cases.js b/src/components/popover/__tests__/popover-test-cases.js
index 9840a70..b9be811 100644
--- a/src/components/popover/__tests__/popover-test-cases.js
+++ b/src/components/popover/__tests__/popover-test-cases.js
@@ -292,7 +292,7 @@ noDisplayCases.allProps = {
     getAnchorElement: () => {},
     children: getPopoverContent(),
     placement: 'left',
-    padding: false,
+    padding: 'none',
     hasPointer: false,
     hideWhenAnchorIsOffscreen: true,
     allowPlacementAxisChange: false,
diff --git a/src/components/tab-list/__tests__/__snapshots__/tab-list.test.js.snap b/src/components/tab-list/__tests__/__snapshots__/tab-list.test.js.snap
index 3f7aa24..cbc0cc8 100644
--- a/src/components/tab-list/__tests__/__snapshots__/tab-list.test.js.snap
+++ b/src/components/tab-list/__tests__/__snapshots__/tab-list.test.js.snap
@@ -64,6 +64,7 @@ exports[`TabList basic renders 1`] = `
     className="fl"
   >
     <PopoverTrigger
+      block={false}
       content={
         Array [
           <div
@@ -93,7 +94,6 @@ exports[`TabList basic renders 1`] = `
         ]
       }
       disabled={false}
-      display="inline-block"
       onPopoverClose={[Function]}
       onPopoverOpen={[Function]}
       popoverProps={
@@ -185,6 +185,7 @@ exports[`TabList links renders 1`] = `
     className="fl"
   >
     <PopoverTrigger
+      block={false}
       content={
         Array [
           <div
@@ -212,7 +213,6 @@ exports[`TabList links renders 1`] = `
         ]
       }
       disabled={false}
-      display="inline-block"
       onPopoverClose={[Function]}
       onPopoverOpen={[Function]}
       popoverProps={
@@ -304,6 +304,7 @@ exports[`TabList truncate all renders 1`] = `
     className="fl"
   >
     <PopoverTrigger
+      block={false}
       content={
         Array [
           <div
@@ -353,7 +354,6 @@ exports[`TabList truncate all renders 1`] = `
         ]
       }
       disabled={false}
-      display="inline-block"
       onPopoverClose={[Function]}
       onPopoverOpen={[Function]}
       popoverProps={
diff --git a/src/components/tooltip/__tests__/__snapshots__/tooltip.test.js.snap b/src/components/tooltip/__tests__/__snapshots__/tooltip.test.js.snap
index 6c80009..dc9ccfd 100644
--- a/src/components/tooltip/__tests__/__snapshots__/tooltip.test.js.snap
+++ b/src/components/tooltip/__tests__/__snapshots__/tooltip.test.js.snap
@@ -17,22 +17,22 @@ exports[`Tooltip Button child trigger renders as expected 1`] = `
 
 exports[`Tooltip DOM element child renders as expected 1`] = `
 <PopoverTrigger
+  block={false}
   content={
     <div
-      className="px12 py6 txt-s wmax240"
+      className="txt-s wmax240"
     >
       DOM element child
     </div>
   }
   disabled={false}
-  display="inline-block"
   popoverProps={
     Object {
       "alignment": "center",
       "coloring": "light",
       "hasPointer": true,
       "hideWhenAnchorIsOffscreen": true,
-      "padded": false,
+      "padding": "small",
       "passthroughProps": Object {
         "id": "tooltip-1",
         "role": "tooltip",
diff --git a/src/test-cases-app/test-cases-app.js b/src/test-cases-app/test-cases-app.js
index 94d64ad..eb1d66d 100644
--- a/src/test-cases-app/test-cases-app.js
+++ b/src/test-cases-app/test-cases-app.js
@@ -1,3 +1,4 @@
+import 'es6-promise/auto';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ReactTestKitchen from '@mapbox/react-test-kitchen';