diff --git a/docs/manifest.json b/docs/manifest.json
index 8c0881895f142..43b98b1c79878 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -1817,6 +1817,12 @@
"markdown_source": "../packages/token-list/README.md",
"parent": "packages"
},
+ {
+ "title": "@wordpress/ui-utils",
+ "slug": "packages-ui-utils",
+ "markdown_source": "../packages/ui-utils/README.md",
+ "parent": "packages"
+ },
{
"title": "@wordpress/url",
"slug": "packages-url",
diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md
index d0ad061a871a4..80ab0d2f1b4b8 100644
--- a/packages/compose/CHANGELOG.md
+++ b/packages/compose/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+### New Features
+
+- Add `is` utility
+
## 3.4.0 (2019-06-12)
### New Features
diff --git a/packages/ui-utils/.npmrc b/packages/ui-utils/.npmrc
new file mode 100644
index 0000000000000..9cf9495031ecc
--- /dev/null
+++ b/packages/ui-utils/.npmrc
@@ -0,0 +1 @@
+package-lock=false
\ No newline at end of file
diff --git a/packages/ui-utils/CHANGELOG.md b/packages/ui-utils/CHANGELOG.md
new file mode 100644
index 0000000000000..f11e8cd301086
--- /dev/null
+++ b/packages/ui-utils/CHANGELOG.md
@@ -0,0 +1,5 @@
+
+
+## Unreleased
+
+Initial release.
diff --git a/packages/ui-utils/README.md b/packages/ui-utils/README.md
new file mode 100644
index 0000000000000..a3e4825caeb56
--- /dev/null
+++ b/packages/ui-utils/README.md
@@ -0,0 +1,68 @@
+# UI Utils
+
+A collection of utilities useful for building user interfaces.
+
+## API
+
+
+
+# **isDefined**
+
+Checks whether a value is defined, asserting that it is neither
+null nor undefined.
+
+_Parameters_
+
+- _value_ `(T|undefined|null)`: The value to check.
+
+_Returns_
+
+- (unknown type): Whether the value is undefined.
+
+# **isObjectInterpolation**
+
+There's no way to detect whether an `any` type is an `ObjectInterpolation` because `ObjectInterpolation`s
+are merely objects where the keys are either (1) [CSS property names](https://github.com/emotion-js/emotion/blob/a72e6dc0f326b7d3d6067698d433018ee8c4cbf1/packages/serialize/types/index.d.ts#L10),
+(2) [CSS Pseudo property names](https://github.com/emotion-js/emotion/blob/a72e6dc0f326b7d3d6067698d433018ee8c4cbf1/packages/serialize/types/index.d.ts#L18),
+or (3) [anything else where the value for the key is another interpolation](https://github.com/emotion-js/emotion/blob/a72e6dc0f326b7d3d6067698d433018ee8c4cbf1/packages/serialize/types/index.d.ts#L19).
+That means ObjectInterpolations can look all of the following ways:
+
+```js
+const oi = {
+ color: 'black',
+};
+
+const oi = {
+ ':disabled': css`color: black;`,
+};
+
+const oi = {
+ '@media (prefers-color-scheme: light)': css({
+ backgroundColor: 'black',
+ color: 'white',
+ }),
+};
+```
+
+Therefore, this function only accepts `TemplateStringsArray` and `Interpolation` for it's arguments rather than `any`, which
+cannot be refined to an `ObjectInterpolation`. However, given any generic `Interpolation`, we _can_ tell whether it is an
+`ObjectInterpolation` specifically by simply checking whether the value is "plain object" as defined by `lodash`'s `isPlainObject`.
+
+Note: The links above reference a specific commit for **Emotion 10**. Emotion 11 is out but we're stuck on Emotion 10 until we can
+re-write `create-styles` and upgrade all of `@wordpress/components` to use the new version of Emotion.
+
+At that point we'll have to revisit this function anyway as it seems that the `ObjectInterpolation` type no longer
+exists and that the concept has evolved.
+
+_Parameters_
+
+- _value_ (unknown type): The value to check.
+
+_Returns_
+
+- (unknown type): Whether the value is an ObjectInterpolation.
+
+
+
+
+
![Code is Poetry.](https://s.w.org/style/images/codeispoetry.png?1)
diff --git a/packages/ui-utils/package.json b/packages/ui-utils/package.json
new file mode 100644
index 0000000000000..22e5dd6d174f6
--- /dev/null
+++ b/packages/ui-utils/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "@wordpress/ui-utils",
+ "version": "0.0.1",
+ "description": "A collection of simple utilities for building user interfaces.",
+ "author": "The WordPress Contributors",
+ "license": "GPL-2.0-or-later",
+ "keywords": [
+ "wordpress",
+ "gutenberg",
+ "utils"
+ ],
+ "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/ui-utils/README.md",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/ui-utils"
+ },
+ "bugs": {
+ "url": "https://github.com/WordPress/gutenberg/issues"
+ },
+ "files": [
+ "build",
+ "build-module",
+ "build-types",
+ "src",
+ "*.md"
+ ],
+ "main": "build/index.js",
+ "module": "build-module/index.js",
+ "react-native": "src/index",
+ "types": "build-types",
+ "sideEffects": false,
+ "dependencies": {
+ "lodash": "^4.17.19"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/ui-utils/src/index.js b/packages/ui-utils/src/index.js
new file mode 100644
index 0000000000000..6d411d711a52e
--- /dev/null
+++ b/packages/ui-utils/src/index.js
@@ -0,0 +1,2 @@
+export { default as isDefined } from './is-defined';
+export { default as isObjectInterpolation } from './is-object-interpolation';
diff --git a/packages/ui-utils/src/is-defined/index.js b/packages/ui-utils/src/is-defined/index.js
new file mode 100644
index 0000000000000..8e9c0b43b408b
--- /dev/null
+++ b/packages/ui-utils/src/is-defined/index.js
@@ -0,0 +1,13 @@
+/* eslint-disable jsdoc/valid-types */
+/**
+ * Checks whether a value is defined, asserting that it is neither
+ * null nor undefined.
+ *
+ * @template T
+ * @param {T | undefined | null} value The value to check.
+ * @return {value is T} Whether the value is undefined.
+ */
+const isDefined = ( value ) => value !== null && value !== undefined;
+
+export default isDefined;
+/* eslint-enable jsdoc/valid-types */
diff --git a/packages/ui-utils/src/is-object-interpolation/index.js b/packages/ui-utils/src/is-object-interpolation/index.js
new file mode 100644
index 0000000000000..469e84daf0ee8
--- /dev/null
+++ b/packages/ui-utils/src/is-object-interpolation/index.js
@@ -0,0 +1,49 @@
+/* global TemplateStringsArray */
+/**
+ * External dependencies
+ */
+import isPlainObject from 'lodash/isPlainObject';
+
+/* eslint-disable jsdoc/valid-types */
+/**
+ *
+ * There's no way to detect whether an `any` type is an `ObjectInterpolation` because `ObjectInterpolation`s
+ * are merely objects where the keys are either (1) [CSS property names](https://github.com/emotion-js/emotion/blob/a72e6dc0f326b7d3d6067698d433018ee8c4cbf1/packages/serialize/types/index.d.ts#L10),
+ * (2) [CSS Pseudo property names](https://github.com/emotion-js/emotion/blob/a72e6dc0f326b7d3d6067698d433018ee8c4cbf1/packages/serialize/types/index.d.ts#L18),
+ * or (3) [anything else where the value for the key is another interpolation](https://github.com/emotion-js/emotion/blob/a72e6dc0f326b7d3d6067698d433018ee8c4cbf1/packages/serialize/types/index.d.ts#L19).
+ * That means ObjectInterpolations can look all of the following ways:
+ *
+ * ```js
+ * const oi = {
+ * color: 'black',
+ * };
+ *
+ * const oi = {
+ * ':disabled': css`color: black;`,
+ * };
+ *
+ * const oi = {
+ * '@media (prefers-color-scheme: light)': css({
+ * backgroundColor: 'black',
+ * color: 'white',
+ * }),
+ * };
+ * ```
+ *
+ * Therefore, this function only accepts `TemplateStringsArray` and `Interpolation` for it's arguments rather than `any`, which
+ * cannot be refined to an `ObjectInterpolation`. However, given any generic `Interpolation`, we _can_ tell whether it is an
+ * `ObjectInterpolation` specifically by simply checking whether the value is "plain object" as defined by `lodash`'s `isPlainObject`.
+ *
+ * Note: The links above reference a specific commit for **Emotion 10**. Emotion 11 is out but we're stuck on Emotion 10 until we can
+ * re-write `create-styles` and upgrade all of `@wordpress/components` to use the new version of Emotion.
+ *
+ * At that point we'll have to revisit this function anyway as it seems that the `ObjectInterpolation` type no longer
+ * exists and that the concept has evolved.
+ *
+ * @param {TemplateStringsArray | import('create-emotion').Interpolation} value The value to check.
+ * @return {value is import('create-emotion').ObjectInterpolation} Whether the value is an ObjectInterpolation.
+ */
+const isObjectInterpolation = ( value ) => isPlainObject( value );
+/* eslint-enable jsdoc/valid-types */
+
+export default isObjectInterpolation;