Repository: uber-web/baseui
Pull request #983 (2019-03-05 17:33:55)

feat(tag): add solid, outlined tags, click handlers


Patch:
diff --git a/documentation-site/pages/components/tag.mdx b/documentation-site/pages/components/tag.mdx
index 94d43a379..bbcd01143 100644
--- a/documentation-site/pages/components/tag.mdx
+++ b/documentation-site/pages/components/tag.mdx
@@ -11,9 +11,11 @@ import Layout from '../../components/layout';
 
 import Basic from 'examples/tag/basic.js';
 import Kinds from 'examples/tag/kinds.js';
-import CustomColor from 'examples/tag/custom-color.js';
+import Clickable from 'examples/tag/clickable.js';
 import Disabled from 'examples/tag/disabled.js';
-import Uncloseable from 'examples/tag/uncloseable.js';
+import Noncloseable from 'examples/tag/non-closeable.js';
+import NoncloseableNonclickable from 'examples/tag/clickable-non-closeable.js';
+import CustomColor from 'examples/tag/custom-color.js';
 
 import Overrides from '../../components/overrides';
 import {Tag} from 'baseui/tag';
@@ -35,20 +37,28 @@ Compact UI to display a collection of consice information.
   <Basic />
 </Example>
 
-<Example title="Tag with all available kinds" path="examples/tag/kinds.js">
+<Example title="Tag closeable/non-clickable" path="examples/tag/kinds.js">
   <Kinds />
 </Example>
 
-<Example title="Tag Custom Color example" path="examples/tag/custom-color.js">
-  <CustomColor />
+<Example title="Tag closeable/clickable" path="examples/tag/clickable.js">
+  <Clickable />
 </Example>
 
-<Example title="Tag with disabled state" path="examples/tag/disabled.js">
+<Example title="Tag in a disabled state" path="examples/tag/disabled.js">
   <Disabled />
 </Example>
 
-<Example title="Tag uncloseable example" path="examples/tag/uncloseable.js">
-  <Uncloseable />
+<Example title="Tag non-closeable/non-clickable" path="examples/tag/non-closeable.js">
+  <Noncloseable />
+</Example>
+
+<Example title="Tag non-closeable/clickable" path="examples/tag/clickable-non-closeable.js">
+  <NoncloseableNonclickable />
+</Example>
+
+<Example title="Tag Custom Color example" path="examples/tag/custom-color.js">
+  <CustomColor />
 </Example>
 
 ## Overrides
diff --git a/documentation-site/static/examples/tag/clickable-non-closeable.js b/documentation-site/static/examples/tag/clickable-non-closeable.js
new file mode 100644
index 000000000..b51628035
--- /dev/null
+++ b/documentation-site/static/examples/tag/clickable-non-closeable.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import {Tag, KIND, VARIANT} from 'baseui/tag';
+
+const kinds = Object.keys(KIND).filter(kind => kind !== KIND.custom);
+const variants = Object.keys(VARIANT);
+const onClick = kind => alert(`${kind} tag is clicked`);
+
+export default () => (
+  <>
+    {variants.map((variant, index) => (
+      <React.Fragment key={index}>
+        {kinds.map((kind, index) => (
+          <Tag
+            key={index}
+            closeable={false}
+            onClick={() => {
+              onClick(kind);
+            }}
+            variant={variant}
+            kind={KIND[kind]}
+          >
+            {KIND[kind]}
+          </Tag>
+        ))}
+        <br />
+      </React.Fragment>
+    ))}
+  </>
+);
diff --git a/documentation-site/static/examples/tag/clickable.js b/documentation-site/static/examples/tag/clickable.js
new file mode 100644
index 000000000..bd25e8948
--- /dev/null
+++ b/documentation-site/static/examples/tag/clickable.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import {Tag, KIND, VARIANT} from 'baseui/tag';
+
+const kinds = Object.keys(KIND).filter(kind => kind !== KIND.custom);
+const variants = Object.keys(VARIANT);
+const onClick = kind => alert(`${kind} tag is clicked`);
+
+export default () => (
+  <>
+    {variants.map((variant, index) => (
+      <React.Fragment key={index}>
+        {kinds.map((kind, index) => (
+          <Tag
+            key={index}
+            onClick={() => {
+              onClick(kind);
+            }}
+            variant={variant}
+            kind={KIND[kind]}
+          >
+            {KIND[kind]}
+          </Tag>
+        ))}
+        <br />
+      </React.Fragment>
+    ))}
+  </>
+);
diff --git a/documentation-site/static/examples/tag/custom-color.js b/documentation-site/static/examples/tag/custom-color.js
index 71fcefce7..6d26defcb 100644
--- a/documentation-site/static/examples/tag/custom-color.js
+++ b/documentation-site/static/examples/tag/custom-color.js
@@ -1,8 +1,26 @@
 import React from 'react';
-import {Tag} from 'baseui/tag';
+import {Tag, KIND, VARIANT} from 'baseui/tag';
 
 export default () => (
-  <Tag color="#333333" kind="custom">
-    custom
-  </Tag>
+  <>
+    <Tag color="#4D26B0" kind={KIND.custom} onClick={() => {}}>
+      custom
+    </Tag>
+    <Tag
+      color="#4D26B0"
+      variant={VARIANT.outlined}
+      kind={KIND.custom}
+      onClick={() => {}}
+    >
+      custom
+    </Tag>
+    <Tag
+      color="#4D26B0"
+      variant={VARIANT.solid}
+      kind={KIND.custom}
+      onClick={() => {}}
+    >
+      custom
+    </Tag>
+  </>
 );
diff --git a/documentation-site/static/examples/tag/disabled.js b/documentation-site/static/examples/tag/disabled.js
index dcb9e5d40..0a74021fd 100644
--- a/documentation-site/static/examples/tag/disabled.js
+++ b/documentation-site/static/examples/tag/disabled.js
@@ -1,19 +1,29 @@
 import React from 'react';
-import {Tag, KIND} from 'baseui/tag';
+import {Tag, KIND, VARIANT} from 'baseui/tag';
+
+const kinds = Object.keys(KIND).filter(kind => kind !== KIND.custom);
+const variants = Object.keys(VARIANT);
+const onClick = kind => alert(`${kind} tag is clicked`);
 
 export default () => (
-  <React.Fragment>
-    <Tag disabled kind={KIND.primary}>
-      {KIND.primary}
-    </Tag>
-    <Tag disabled kind={KIND.warning}>
-      {KIND.warning}
-    </Tag>
-    <Tag disabled kind={KIND.positive}>
-      {KIND.positive}
-    </Tag>
-    <Tag disabled kind={KIND.negative}>
-      {KIND.negative}
-    </Tag>
-  </React.Fragment>
+  <>
+    {variants.map((variant, index) => (
+      <React.Fragment key={index}>
+        {kinds.map((kind, index) => (
+          <Tag
+            key={index}
+            disabled
+            onClick={() => {
+              onClick(kind);
+            }}
+            variant={variant}
+            kind={KIND[kind]}
+          >
+            {KIND[kind]}
+          </Tag>
+        ))}
+        <br />
+      </React.Fragment>
+    ))}
+  </>
 );
diff --git a/documentation-site/static/examples/tag/kinds.js b/documentation-site/static/examples/tag/kinds.js
index 1430b3198..ac7fae59f 100644
--- a/documentation-site/static/examples/tag/kinds.js
+++ b/documentation-site/static/examples/tag/kinds.js
@@ -1,11 +1,20 @@
 import React from 'react';
-import {Tag, KIND} from 'baseui/tag';
+import {Tag, KIND, VARIANT} from 'baseui/tag';
+
+const kinds = Object.keys(KIND).filter(kind => kind !== KIND.custom);
+const variants = Object.keys(VARIANT);
 
 export default () => (
-  <React.Fragment>
-    <Tag kind={KIND.primary}>{KIND.primary}</Tag>
-    <Tag kind={KIND.warning}>{KIND.warning}</Tag>
-    <Tag kind={KIND.positive}>{KIND.positive}</Tag>
-    <Tag kind={KIND.negative}>{KIND.negative}</Tag>
-  </React.Fragment>
+  <>
+    {variants.map((variant, index) => (
+      <React.Fragment key={index}>
+        {kinds.map((kind, index) => (
+          <Tag key={index} variant={variant} kind={KIND[kind]}>
+            {KIND[kind]}
+          </Tag>
+        ))}
+        <br />
+      </React.Fragment>
+    ))}
+  </>
 );
diff --git a/documentation-site/static/examples/tag/non-closeable.js b/documentation-site/static/examples/tag/non-closeable.js
new file mode 100644
index 000000000..a378901c1
--- /dev/null
+++ b/documentation-site/static/examples/tag/non-closeable.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import {Tag, KIND, VARIANT} from 'baseui/tag';
+
+const kinds = Object.keys(KIND).filter(kind => kind !== KIND.custom);
+const variants = Object.keys(VARIANT);
+
+export default () => (
+  <>
+    {variants.map((variant, index) => (
+      <React.Fragment key={index}>
+        {kinds.map((kind, index) => (
+          <Tag
+            key={index}
+            closeable={false}
+            variant={variant}
+            kind={KIND[kind]}
+          >
+            {KIND[kind]}
+          </Tag>
+        ))}
+        <br />
+      </React.Fragment>
+    ))}
+  </>
+);
diff --git a/documentation-site/static/examples/tag/uncloseable.js b/documentation-site/static/examples/tag/uncloseable.js
deleted file mode 100644
index 3612bbf13..000000000
--- a/documentation-site/static/examples/tag/uncloseable.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import React from 'react';
-import {Tag, KIND} from 'baseui/tag';
-
-export default () => (
-  <Tag kind={KIND.primary} closeable={false}>
-    Label
-  </Tag>
-);
diff --git a/src/styles/types.js b/src/styles/types.js
index e9f406253..550484d90 100644
--- a/src/styles/types.js
+++ b/src/styles/types.js
@@ -190,6 +190,16 @@ export type ColorsT = {
   shadowFocus: string,
   shadowError: string,
 
+  // Tag
+  tagBackground: string,
+  tagNeutralBackground: string,
+  tagPrimaryBackground: string,
+  tagPositiveBackground: string,
+  tagWarningBackground: string,
+  tagNegativeBackground: string,
+  tagRGBGradient: string,
+  tagRGBGradientSecondary: string,
+
   // Table
   tableHeadBackgroundColor: string,
 
diff --git a/src/tag/constants.js b/src/tag/constants.js
index c1be17d1b..ec7c9d9f2 100644
--- a/src/tag/constants.js
+++ b/src/tag/constants.js
@@ -9,12 +9,19 @@ LICENSE file in the root directory of this source tree.
 import type {TagKindT} from './types.js';
 
 export const COLOR_STYLE_KEYS = {
-  primary: 'primary400',
-  warning: 'warning400',
-  positive: 'positive400',
-  negative: 'negative400',
+  neutral: 'tagNeutralBackground',
+  primary: 'tagPrimaryBackground',
+  positive: 'tagPositiveBackground',
+  warning: 'tagWarningBackground',
+  negative: 'tagNegativeBackground',
 };
 
+export const VARIANT = Object.freeze({
+  light: 'light',
+  outlined: 'outlined',
+  solid: 'solid',
+});
+
 export const KIND: {[TagKindT]: string} = Object.keys({
   ...COLOR_STYLE_KEYS,
   custom: null,
diff --git a/src/tag/index.js b/src/tag/index.js
index f24a247d8..ce1a93573 100644
--- a/src/tag/index.js
+++ b/src/tag/index.js
@@ -13,5 +13,5 @@ export {
   ActionIcon as StyledActionIcon,
   Text as StyledText,
 } from './styled-components.js';
-export {COLOR_STYLE_KEYS, KIND} from './constants.js';
+export {COLOR_STYLE_KEYS, KIND, VARIANT} from './constants.js';
 export * from './types.js';
diff --git a/src/tag/styled-components.js b/src/tag/styled-components.js
index 5b106b325..8d827b5e9 100644
--- a/src/tag/styled-components.js
+++ b/src/tag/styled-components.js
@@ -5,55 +5,160 @@ This source code is licensed under the MIT license found in the
 LICENSE file in the root directory of this source tree.
 */
 // @flow
-import {COLOR_STYLE_KEYS} from './constants.js';
+import {COLOR_STYLE_KEYS, KIND, VARIANT} from './constants.js';
 import {styled, hexToRgb, type ThemeT} from '../styles/index.js';
 import type {SharedPropsT} from './types.js';
 
-export function getBackgroundColor(props: SharedPropsT) {
-  const {$color, $isActive, $isFocused, $kind, $theme} = props;
-  const color = getColor($theme, $kind, $color);
-  return hexToRgb(color, $isFocused || $isActive ? '0.2' : '0.06');
-}
-
 export function getColor(
   $theme?: ThemeT,
   $kind?: string = COLOR_STYLE_KEYS.primary,
   $color?: string = '#000',
 ) {
-  if ($kind === 'custom') {
-    return $color;
-  }
-  if ($theme && $theme.colors && $theme.colors[COLOR_STYLE_KEYS[$kind]]) {
+  if ($theme && $theme.colors[COLOR_STYLE_KEYS[$kind]]) {
     return $theme.colors[COLOR_STYLE_KEYS[$kind]];
   }
+  return $color;
+}
+
+function getAlpha(props: SharedPropsT, hovered: boolean = false) {
+  if (hovered) {
+    return '0.8';
+  }
+  return '0.92';
 }
 
-export function getHoverBackgroundColor(props: SharedPropsT) {
-  const {$color, $disabled, $kind, $theme} = props;
+function getActionAlpha(
+  props: SharedPropsT,
+  hovered: boolean = false,
+  secondary: boolean = false,
+) {
+  const {$clickable, $variant = VARIANT.light} = props;
+  if ($variant === VARIANT.solid && (!$clickable && hovered) && secondary) {
+    return '0.24';
+  }
+  if (hovered) {
+    return '0.6';
+  }
+  return '0.92';
+}
+
+function getBackgroundColor(props: SharedPropsT, hovered: boolean = false) {
+  const {$color, $kind, $variant, $theme} = props;
+  if ($variant === VARIANT.outlined) {
+    return $theme.colors.tagBackground;
+  }
+
   const color = getColor($theme, $kind, $color);
-  return $disabled ? null : hexToRgb(color, '0.2');
+  if ($variant === VARIANT.solid && !hovered) {
+    return color;
+  }
+
+  const alpha = getAlpha(props, hovered);
+  return `linear-gradient(0deg, 
+    rgba(${$theme.colors.tagRGBGradient}, ${alpha}), 
+    rgba(${$theme.colors.tagRGBGradient}, ${alpha})), 
+    ${color}`;
+}
+
+function getBorderColor(props: SharedPropsT, hovered: boolean = false) {
+  const {$color, $clickable, $kind, $variant, $theme} = props;
+  if ($variant !== VARIANT.outlined) {
+    return 'transparent';
+  }
+  const color = getColor($theme, $kind, $color);
+  return hexToRgb(color, $clickable && hovered ? '0.6' : '1');
+}
+
+function getBorderWidth(props: SharedPropsT) {
+  const {$variant} = props;
+  if ($variant === VARIANT.outlined) {
+    return '2px';
+  }
+  return '0';
+}
+
+function getActionBackgroundColor(
+  props: SharedPropsT,
+  hovered: boolean = false,
+) {
+  const {$color, $clickable, $kind, $variant, $theme} = props;
+  const color = getColor($theme, $kind, $color);
+  if (!hovered) {
+    return 'transparent';
+  }
+  if ($variant === VARIANT.outlined) {
+    return getBorderColor(props, hovered);
+  }
+  let alpha;
+  if ($variant === VARIANT.solid && (!$clickable && hovered)) {
+    alpha = getActionAlpha(props, hovered, true);
+    return `linear-gradient(0deg, 
+      rgba(${$theme.colors.tagRGBGradientSecondary}, ${alpha}), 
+      rgba(${$theme.colors.tagRGBGradientSecondary}, ${alpha})), 
+      ${color}`;
+  }
+  alpha = getActionAlpha(props, hovered);
+  return `linear-gradient(0deg, 
+    rgba(${$theme.colors.tagRGBGradient}, ${alpha}), 
+    rgba(${$theme.colors.tagRGBGradient}, ${alpha})), 
+    ${color}`;
+}
+
+function getActionFontColor(props: SharedPropsT, hovered: boolean = false) {
+  const {$variant, $theme} = props;
+  if (hovered && $variant === VARIANT.outlined && $theme) {
+    return $theme.colors.mono200;
+  }
+  return null;
+}
+
+function getFontColor(props: SharedPropsT, hovered) {
+  const {
+    $theme,
+    $kind = KIND.primary,
+    $color = '#000',
+    $variant = VARIANT.light,
+  } = props;
+  if ($variant === VARIANT.solid && !hovered) {
+    return $theme.colors.mono200;
+  }
+  if ($kind === 'custom') {
+    return $color;
+  }
+  if ($theme.colors[COLOR_STYLE_KEYS[$kind]]) {
+    return $theme.colors[COLOR_STYLE_KEYS[$kind]];
+  }
 }
 
 export const Action = styled('span', props => {
-  const {$disabled, $theme} = props;
+  const {$disabled, $variant, $theme} = props;
+  const hoverStyles = $disabled
+    ? {}
+    : {
+        background: getActionBackgroundColor(props, true),
+        color: getActionFontColor(props, true),
+      };
   return {
-    display: 'flex',
     alignItems: 'center',
-    paddingTop: '8px',
-    paddingBottom: '8px',
-    paddingLeft: '8px',
-    paddingRight: '8px',
     borderBottomRightRadius: $theme.borders.useRoundedCorners
       ? $theme.borders.radius400
       : '0px',
     borderTopRightRadius: $theme.borders.useRoundedCorners
       ? $theme.borders.radius400
       : '0px',
-    marginLeft: '8px',
     cursor: $disabled ? 'not-allowed' : 'pointer',
-    ':hover': {
-      backgroundColor: getBackgroundColor(props),
-    },
+    display: 'flex',
+    marginLeft: '8px',
+    outline: 'none',
+    paddingTop: $variant === VARIANT.outlined ? '5px' : '7px',
+    paddingBottom: $variant === VARIANT.outlined ? '5px' : '7px',
+    paddingLeft: '8px',
+    paddingRight: '8px',
+    transitionProperty: 'all',
+    transitionDuration: $theme.animation.timing100,
+    transitionTimingFunction: $theme.animation.easeOutCurve,
+    ':hover': hoverStyles,
+    ':focus': hoverStyles,
   };
 });
 
@@ -71,43 +176,52 @@ export const Text = styled('span', props => {
 });
 
 export const Root = styled('span', props => {
-  const {$color, $disabled, $kind, $theme, $closeable} = props;
+  const {$disabled, $theme, $closeable, $clickable} = props;
   const {
-    sizing: {scale800, scale100, scale500},
+    sizing: {scale0, scale800, scale500},
     typography: {font250},
   } = $theme;
-  const color = getColor($theme, $kind, $color);
   const paddingRightIfNotCloseable = $closeable ? {} : {paddingRight: scale500};
 
+  const hoverStyles =
+    $disabled || $clickable === false
+      ? {}
+      : {
+          background: getBackgroundColor(props, true),
+          borderColor: getBorderColor(props, true),
+          color: getFontColor(props, true),
+        };
+
   return {
     ...font250,
     ...paddingRightIfNotCloseable,
+    alignItems: 'center',
+    background: getBackgroundColor(props),
+    borderColor: getBorderColor(props),
+    borderStyle: 'solid',
+    borderWidth: getBorderWidth(props),
+    borderRadius: $theme.borders.useRoundedCorners
+      ? $theme.borders.radius400
+      : '0px',
+    boxSizing: 'border-box',
+    color: getFontColor(props),
+    cursor: $disabled ? 'not-allowed' : $clickable ? 'pointer' : 'default',
     display: 'inline-flex',
     height: scale800,
+    justifyContent: 'space-between',
     marginTop: '5px',
     marginBottom: '5px',
     marginLeft: '5px',
     marginRight: '5px',
-    boxSizing: 'border-box',
-    alignItems: 'center',
-    justifyContent: 'space-between',
-    paddingTop: scale100,
-    paddingBottom: scale100,
+    paddingTop: scale0,
+    paddingBottom: scale0,
     paddingLeft: scale500,
-    borderWidth: '1px',
-    borderColor: color,
-    color: color,
     opacity: $disabled ? '.56' : null,
-    borderRadius: $theme.borders.useRoundedCorners
-      ? $theme.borders.radius400
-      : '0px',
-    cursor: $disabled ? 'not-allowed' : 'auto',
-    backgroundColor: getBackgroundColor(props),
-    ':hover':
-      $closeable === false
-        ? {}
-        : {
-            backgroundColor: getHoverBackgroundColor(props),
-          },
+    outline: 'none',
+    transitionProperty: 'backgroundColor, borderColor',
+    transitionDuration: $theme.animation.timing100,
+    transitionTimingFunction: $theme.animation.easeOutCurve,
+    ':hover': hoverStyles,
+    ':focus': hoverStyles,
   };
 });
diff --git a/src/tag/tag.js b/src/tag/tag.js
index 30fd3fda5..23e59ebe7 100644
--- a/src/tag/tag.js
+++ b/src/tag/tag.js
@@ -7,37 +7,68 @@ LICENSE file in the root directory of this source tree.
 // @flow
 import React from 'react';
 import {getOverrides} from '../helpers/overrides.js';
-import type {PropsT} from './types.js';
 import {
   Action as StyledAction,
   Root as StyledRoot,
   ActionIcon as StyledActionIcon,
   Text as StyledText,
 } from './styled-components.js';
-import type {SharedPropsT} from './types.js';
+import {KIND, VARIANT} from './constants.js';
+import type {PropsT, SharedPropsArgT} from './types.js';
 
 class Tag extends React.Component<PropsT, {}> {
   static defaultProps = {
-    overrides: {},
-    onActionClick: () => {},
     closeable: true,
     disabled: false,
     isFocused: false,
     isHovered: false,
-    kind: 'primary',
+    onActionClick: () => {},
+    onActionKeyDown: () => {},
+    onClick: null,
+    onKeyDown: null,
+    overrides: {},
+    kind: KIND.primary,
+    variant: VARIANT.light,
+  };
+
+  handleKeyDown = (event: KeyboardEvent) => {
+    if (event.currentTarget !== event.target) {
+      return;
+    }
+    const {onClick, onKeyDown} = this.props;
+    const key = event.key;
+    if (onClick && key === 'Enter') {
+      onClick(event);
+    }
+    if (onKeyDown) {
+      onKeyDown(event);
+    }
+  };
+
+  handleActionKeyDown = (event: KeyboardEvent) => {
+    const {onActionClick, onActionKeyDown} = this.props;
+    const key = event.key;
+    if (onActionClick && key === 'Enter') {
+      onActionClick(event);
+    }
+    if (onActionKeyDown) {
+      onActionKeyDown(event);
+    }
   };
 
   render() {
     const {
-      overrides = {},
+      children,
       closeable,
       color,
       disabled,
       isFocused,
       isHovered,
-      children,
       kind,
       onActionClick,
+      onClick,
+      overrides = {},
+      variant,
     } = this.props;
     const [Root, rootProps] = getOverrides(overrides.Root, StyledRoot);
     const [Action, actionProps] = getOverrides(overrides.Action, StyledAction);
@@ -45,27 +76,51 @@ class Tag extends React.Component<PropsT, {}> {
       overrides.ActionIcon,
       StyledActionIcon,
     );
-    const events = disabled
+    const clickable = typeof onClick === 'function';
+    const rootHandlers = disabled
+      ? {}
+      : {
+          onClick: onClick,
+          onKeyDown: this.handleKeyDown,
+        };
+    const actionHandlers = disabled
       ? {}
       : {
-          onClick: e => onActionClick(e, children),
+          onClick: onActionClick,
+          onKeyDown: this.handleActionKeyDown,
         };
-    const sharedProps: SharedPropsT = {
+    const sharedProps: SharedPropsArgT = {
+      $clickable: clickable,
       $closeable: closeable,
       $color: color,
       $disabled: disabled,
       $isFocused: isFocused,
       $isHovered: isHovered,
       $kind: kind,
+      $variant: variant,
     };
     return (
-      <Root {...sharedProps} {...rootProps}>
+      <Root
+        aria-label="button"
+        role="button"
+        tabIndex={clickable ? '0' : null}
+        {...rootHandlers}
+        {...sharedProps}
+        {...rootProps}
+      >
         <StyledText>{children}</StyledText>
         {closeable ? (
-          <Action {...sharedProps} {...actionProps} {...events}>
+          <Action
+            aria-label="close button"
+            role="button"
+            tabIndex={'0'}
+            {...actionHandlers}
+            {...sharedProps}
+            {...actionProps}
+          >
             <ActionIcon
-              width={'8'}
-              height={'8'}
+              width={'10'}
+              height={'10'}
               viewBox={'0 0 8 8'}
               fill={'none'}
               xmlns={'http://www.w3.org/2000/svg'}
diff --git a/src/tag/types.js b/src/tag/types.js
index 7f05028b3..4c684a26c 100644
--- a/src/tag/types.js
+++ b/src/tag/types.js
@@ -7,11 +7,13 @@ LICENSE file in the root directory of this source tree.
 // @flow
 
 import type {OverrideT} from '../helpers/overrides.js';
-import {COLOR_STYLE_KEYS} from './constants.js';
+import {KIND, VARIANT} from './constants.js';
 
-export const TagKind = Object.freeze({...COLOR_STYLE_KEYS, custom: null});
+export const TagKind = Object.freeze(KIND);
+export const TagVariant = Object.freeze(VARIANT);
 
 export type TagKindT = $Keys<typeof TagKind>;
+export type TagVariantT = $Values<typeof TagVariant>;
 
 export type OverridesT = {
   Root?: OverrideT<*>,
@@ -27,20 +29,27 @@ export type PropsT = {
   disabled?: boolean,
   isFocused?: boolean,
   isHovered?: boolean,
+  /** Defines tags look by purpose. Set it to one of KIND[key] values. Defaults to KIND.primary */
   kind?: TagKindT,
+  /** Defines tags look. Set it to one of VARIANT[key] values. Defaults to VARIANT.light */
+  variant?: TagVariantT,
   /** Component or String value for label of tag. Default is empty string. */
   children?: React$Node,
   /** The color theme to be applied to a Tag. Default is `KIND.primary`. */
   color?: string,
-  /** Handler for events on Action button element. `children` provides which Tag was clicked. */
-  onActionClick: (
-    e: SyntheticEvent<HTMLInputElement>,
-    children?: React$Node,
-  ) => void,
+  /** onClick handler for the action button element. */
+  onActionClick: (e: Event, children?: React$Node) => void,
+  /** keydown handler for the action button element. */
+  onActionKeyDown: (e: Event, children?: React$Node) => void,
+  /** onClick handler for the tag. Passing an onClick handler also makes the tag clickable. */
+  onClick?: null | ((event: Event) => mixed),
+  /** onkeydown handler for the tag. */
+  onKeyDown?: null | ((event: Event) => mixed),
   $theme?: *,
 };
 
-export type SharedPropsT = {
+export type SharedPropsArgT = {
+  $clickable?: boolean,
   $closeable?: boolean,
   $color?: string,
   $disabled?: boolean,
@@ -48,5 +57,9 @@ export type SharedPropsT = {
   $isFocused?: boolean,
   $isHovered?: boolean,
   $kind?: string,
-  $theme?: *,
+  $variant?: string,
+};
+
+export type SharedPropsT = SharedPropsArgT & {
+  $theme: *,
 };
diff --git a/src/themes/creator.js b/src/themes/creator.js
index f0f9a81cb..d9c3a986f 100644
--- a/src/themes/creator.js
+++ b/src/themes/creator.js
@@ -211,6 +211,16 @@ export default function createTheme(
       notificationNegativeBackground: primitives.negative50,
       notificationNegativeText: primitives.negative500,
 
+      // Tag
+      tagBackground: primitives.mono100,
+      tagNeutralBackground: primitives.mono900,
+      tagPrimaryBackground: primitives.primary400,
+      tagPositiveBackground: primitives.positive400,
+      tagWarningBackground: primitives.warning400,
+      tagNegativeBackground: primitives.negative400,
+      tagRGBGradient: '255, 255, 255',
+      tagRGBGradientSecondary: '0, 0, 0',
+
       // Table
       tableHeadBackgroundColor: primitives.mono100,
 
diff --git a/src/themes/dark-theme.js b/src/themes/dark-theme.js
index 58f1c896b..fb5971a6e 100644
--- a/src/themes/dark-theme.js
+++ b/src/themes/dark-theme.js
@@ -82,6 +82,14 @@ export const DarkTheme = createTheme(
       listIconFill: primitives.mono100,
       listBorder: primitives.mono500,
 
+      // Tag
+      tagBackground: primitives.mono1000,
+      tagNeutralBackground: primitives.mono600,
+      tagPrimaryBackground: primitives.primary500,
+      tagPositiveBackground: primitives.positive500,
+      tagWarningBackground: primitives.warning500,
+      tagNegativeBackground: primitives.negative500,
+
       // Tick
       tickFill: 'transparent',
       tickFillHover: primitives.mono200,


Test patch:
diff --git a/src/tag/__tests__/__snapshots__/tag.test.js.snap b/src/tag/__tests__/__snapshots__/tag.test.js.snap
index 727f9c8b8..f2cece184 100644
--- a/src/tag/__tests__/__snapshots__/tag.test.js.snap
+++ b/src/tag/__tests__/__snapshots__/tag.test.js.snap
@@ -8,17 +8,33 @@ exports[`Stateless tag should render component: Component has correct render 1`]
   isHovered={false}
   kind="primary"
   onActionClick={[MockFunction]}
+  onActionKeyDown={[Function]}
+  onClick={null}
+  onKeyDown={null}
   overrides={Object {}}
+  variant="light"
 >
   <MockStyledComponent
+    $clickable={false}
     $closeable={true}
     $disabled={false}
     $isFocused={false}
     $isHovered={false}
     $kind="primary"
+    $variant="light"
+    aria-label="button"
+    onClick={null}
+    onKeyDown={[Function]}
+    role="button"
+    tabIndex={null}
   >
     <span
+      aria-label="button"
+      onClick={null}
+      onKeyDown={[Function]}
+      role="button"
       styled-component="true"
+      tabIndex={null}
     >
       <MockStyledComponent>
         <span
@@ -28,30 +44,40 @@ exports[`Stateless tag should render component: Component has correct render 1`]
         </span>
       </MockStyledComponent>
       <MockStyledComponent
+        $clickable={false}
         $closeable={true}
         $disabled={false}
         $isFocused={false}
         $isHovered={false}
         $kind="primary"
-        onClick={[Function]}
+        $variant="light"
+        aria-label="close button"
+        onClick={[MockFunction]}
+        onKeyDown={[Function]}
+        role="button"
+        tabIndex="0"
       >
         <span
-          onClick={[Function]}
+          aria-label="close button"
+          onClick={[MockFunction]}
+          onKeyDown={[Function]}
+          role="button"
           styled-component="true"
+          tabIndex="0"
         >
           <MockStyledComponent
             fill="none"
-            height="8"
+            height="10"
             viewBox="0 0 8 8"
-            width="8"
+            width="10"
             xmlns="http://www.w3.org/2000/svg"
           >
             <svg
               fill="none"
-              height="8"
+              height="10"
               styled-component="true"
               viewBox="0 0 8 8"
-              width="8"
+              width="10"
               xmlns="http://www.w3.org/2000/svg"
             >
               <path
diff --git a/src/tag/__tests__/tag.test.js b/src/tag/__tests__/tag.test.js
index 091171a46..27a29b3ca 100644
--- a/src/tag/__tests__/tag.test.js
+++ b/src/tag/__tests__/tag.test.js
@@ -48,7 +48,6 @@ describe('Stateless tag', function() {
       actionIcon.first().simulate('click');
       expect(allProps.onActionClick).toHaveBeenCalledWith(
         allProps.onActionClick.mock.calls[0][0],
-        children,
       );
     });
   });