diff --git a/packages/fluentui/CHANGELOG.md b/packages/fluentui/CHANGELOG.md
index 4a945f2feabb02..de786dde943242 100644
--- a/packages/fluentui/CHANGELOG.md
+++ b/packages/fluentui/CHANGELOG.md
@@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## Features
- Add `borderActive4` color slot @notandrew ([#17391](https://github.com/microsoft/fluentui/pull/17391))
+- Add `Pill` base componet @assuncaocharles ([#17500](https://github.com/microsoft/fluentui/pull/17500))
## Documentation
- Update left nav in UI Builder to separate add components from navigator @codepretty ([#17002](https://github.com/microsoft/fluentui/pull/17002))
diff --git a/packages/fluentui/docs/src/examples/components/Pill/State/PillExampleDisabled.tsx b/packages/fluentui/docs/src/examples/components/Pill/State/PillExampleDisabled.tsx
new file mode 100644
index 00000000000000..c98c8319a3f4c9
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/State/PillExampleDisabled.tsx
@@ -0,0 +1,6 @@
+import * as React from 'react';
+import { Pill } from '@fluentui/react-northstar';
+
+const PillExampleDisabled = () => This is a disabled Pill;
+
+export default PillExampleDisabled;
diff --git a/packages/fluentui/docs/src/examples/components/Pill/State/index.tsx b/packages/fluentui/docs/src/examples/components/Pill/State/index.tsx
new file mode 100644
index 00000000000000..ef3674b06fa490
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/State/index.tsx
@@ -0,0 +1,16 @@
+import * as React from 'react';
+
+import ComponentExample from '../../../../components/ComponentDoc/ComponentExample';
+import ExampleSection from '../../../../components/ComponentDoc/ExampleSection';
+
+const State = () => (
+
+
+
+);
+
+export default State;
diff --git a/packages/fluentui/docs/src/examples/components/Pill/Types/PillExample.tsx b/packages/fluentui/docs/src/examples/components/Pill/Types/PillExample.tsx
new file mode 100644
index 00000000000000..f8eda43032ea0f
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/Types/PillExample.tsx
@@ -0,0 +1,6 @@
+import * as React from 'react';
+import { Pill } from '@fluentui/react-northstar';
+
+const PillExample = () => This is a default Pill;
+
+export default PillExample;
diff --git a/packages/fluentui/docs/src/examples/components/Pill/Types/index.tsx b/packages/fluentui/docs/src/examples/components/Pill/Types/index.tsx
new file mode 100644
index 00000000000000..2fb95251c229c3
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/Types/index.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react';
+
+import ComponentExample from '../../../../components/ComponentDoc/ComponentExample';
+import ExampleSection from '../../../../components/ComponentDoc/ExampleSection';
+
+const Types = () => (
+
+
+
+);
+
+export default Types;
diff --git a/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleAppearance.tsx b/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleAppearance.tsx
new file mode 100644
index 00000000000000..b9cf4bbea6a0e7
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleAppearance.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react';
+import { Pill, Flex } from '@fluentui/react-northstar';
+
+const PillAppearanceExample = () => (
+
+ Filled Pill (Default)
+ Inverted Pill
+ Outlined Pill
+
+);
+
+export default PillAppearanceExample;
diff --git a/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleRectangular.tsx b/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleRectangular.tsx
new file mode 100644
index 00000000000000..171a34b5e9d86c
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleRectangular.tsx
@@ -0,0 +1,6 @@
+import * as React from 'react';
+import { Pill } from '@fluentui/react-northstar';
+
+const PillRectangularExample = () => Pill Content;
+
+export default PillRectangularExample;
diff --git a/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleSizes.tsx b/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleSizes.tsx
new file mode 100644
index 00000000000000..1a204691936c0a
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/Variations/PillExampleSizes.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import { Pill, Flex, PillProps } from '@fluentui/react-northstar';
+
+const PillSizesExample = () => (
+
+ {['smaller', 'small', 'medium'].map((size: PillProps['size']) => (
+
+ {size} pill
+
+ ))}
+
+);
+
+export default PillSizesExample;
diff --git a/packages/fluentui/docs/src/examples/components/Pill/Variations/index.tsx b/packages/fluentui/docs/src/examples/components/Pill/Variations/index.tsx
new file mode 100644
index 00000000000000..7eb61ed7d6010a
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/Variations/index.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+
+import ComponentExample from '../../../../components/ComponentDoc/ComponentExample';
+import ExampleSection from '../../../../components/ComponentDoc/ExampleSection';
+
+const Variations = () => (
+
+
+
+
+
+);
+
+export default Variations;
diff --git a/packages/fluentui/docs/src/examples/components/Pill/index.tsx b/packages/fluentui/docs/src/examples/components/Pill/index.tsx
new file mode 100644
index 00000000000000..6694394cc809e7
--- /dev/null
+++ b/packages/fluentui/docs/src/examples/components/Pill/index.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import Types from './Types';
+import Variations from './Variations';
+import State from './State';
+
+const PillExamples = () => (
+ <>
+
+
+
+ >
+);
+
+export default PillExamples;
diff --git a/packages/fluentui/react-northstar/src/components/Pill/Pill.tsx b/packages/fluentui/react-northstar/src/components/Pill/Pill.tsx
new file mode 100644
index 00000000000000..f4da514038c0a1
--- /dev/null
+++ b/packages/fluentui/react-northstar/src/components/Pill/Pill.tsx
@@ -0,0 +1,118 @@
+import * as PropTypes from 'prop-types';
+import * as React from 'react';
+import * as customPropTypes from '@fluentui/react-proptypes';
+import { UIComponentProps, ContentComponentProps, commonPropTypes, SizeValue, createShorthand } from '../../utils';
+import { ShorthandValue, FluentComponentStaticProps } from '../../types';
+import { BoxProps } from '../Box/Box';
+
+import {
+ ComponentWithAs,
+ useAccessibility,
+ getElementType,
+ useStyles,
+ useTelemetry,
+ useFluentContext,
+ useUnhandledProps,
+} from '@fluentui/react-bindings';
+import { PillContent } from './PillContent';
+
+export interface PillProps extends UIComponentProps, ContentComponentProps> {
+ /**
+ * A Pill can be sized.
+ */
+ size?: Extract;
+
+ /**
+ * A Pill can be rectangular
+ */
+ rectangular?: boolean;
+
+ /**
+ * A Pill can be filled, inverted or outline
+ */
+ appearance?: 'filled' | 'inverted' | 'outline';
+
+ /**
+ * A Pill can be disbled
+ */
+ disabled?: boolean;
+}
+
+export type PillStylesProps = Required>;
+
+export const pillClassName = 'ui-pill';
+
+/**
+ * THIS COMPONENT IS STILL IN DEVELOPMENT AND IS NOT READY FOR PRODUCTION
+ * Pills should be used when representing an input, as a way to filter content, or to represent an attribute.
+ */
+export const Pill: ComponentWithAs<'span', PillProps> & FluentComponentStaticProps = props => {
+ const context = useFluentContext();
+ const { setStart, setEnd } = useTelemetry(Pill.displayName, context.telemetry);
+ setStart();
+
+ const { className, design, styles, variables, appearance, size, rectangular, children, content, disabled } = props;
+
+ const ElementType = getElementType(props);
+ const unhandledProps = useUnhandledProps(Pill.handledProps, props);
+
+ const getA11yProps = useAccessibility(props.accessibility, {
+ debugName: Pill.displayName,
+ mapPropsToBehavior: () => ({}),
+ rtl: context.rtl,
+ });
+
+ const { classes } = useStyles(Pill.displayName, {
+ className: pillClassName,
+ mapPropsToStyles: () => ({
+ appearance,
+ size,
+ rectangular,
+ disabled,
+ }),
+ mapPropsToInlineStyles: () => ({
+ className,
+ design,
+ styles,
+ variables,
+ }),
+ rtl: context.rtl,
+ });
+
+ const element = getA11yProps.unstable_wrapWithFocusZone(
+
+ {createShorthand(PillContent, content || {}, {
+ defaultProps: () => ({
+ children,
+ size,
+ }),
+ })}
+ ,
+ );
+
+ setEnd();
+
+ return element;
+};
+
+Pill.defaultProps = {
+ as: 'span',
+};
+
+Pill.propTypes = {
+ ...commonPropTypes.createCommon(),
+ content: customPropTypes.shorthandAllowingChildren,
+ size: PropTypes.oneOf(['small', 'smaller', 'medium']),
+ rectangular: PropTypes.bool,
+ disabled: PropTypes.bool,
+ appearance: PropTypes.oneOf(['filled', 'inverted', 'outline']),
+};
+
+Pill.displayName = 'Pill';
+
+Pill.handledProps = Object.keys(Pill.propTypes) as any;
diff --git a/packages/fluentui/react-northstar/src/components/Pill/PillContent.tsx b/packages/fluentui/react-northstar/src/components/Pill/PillContent.tsx
new file mode 100644
index 00000000000000..fb17bbc0f05118
--- /dev/null
+++ b/packages/fluentui/react-northstar/src/components/Pill/PillContent.tsx
@@ -0,0 +1,96 @@
+import * as React from 'react';
+import { Accessibility } from '@fluentui/accessibility';
+import {
+ ComponentWithAs,
+ getElementType,
+ useUnhandledProps,
+ useAccessibility,
+ useFluentContext,
+ useStyles,
+ useTelemetry,
+} from '@fluentui/react-bindings';
+import {
+ childrenExist,
+ UIComponentProps,
+ ChildrenComponentProps,
+ ContentComponentProps,
+ commonPropTypes,
+ rtlTextContainer,
+ SizeValue,
+} from '../../utils';
+
+import { FluentComponentStaticProps } from '../../types';
+
+export interface PillContentProps extends UIComponentProps, ChildrenComponentProps, ContentComponentProps {
+ /**
+ * Accessibility behavior if overridden by the user.
+ */
+ accessibility?: Accessibility;
+
+ /**
+ * A Pill can be sized.
+ */
+ size?: Extract;
+}
+
+export type PillContentStylesProps = Required>;
+export const pillContentClassName = 'ui-pillcontent';
+
+/**
+ * A PillContent allows user to classify content.
+ */
+export const PillContent: ComponentWithAs<'span', PillContentProps> &
+ FluentComponentStaticProps = props => {
+ const context = useFluentContext();
+ const { setStart, setEnd } = useTelemetry(PillContent.displayName, context.telemetry);
+ setStart();
+
+ const { accessibility, children, className, content, design, styles, variables, size } = props;
+
+ const getA11Props = useAccessibility(accessibility, {
+ debugName: PillContent.displayName,
+ rtl: context.rtl,
+ });
+
+ const { classes } = useStyles(PillContent.displayName, {
+ className: pillContentClassName,
+ mapPropsToStyles: () => ({ size }),
+ mapPropsToInlineStyles: () => ({ className, design, styles, variables }),
+ rtl: context.rtl,
+ });
+
+ const ElementType = getElementType(props);
+ const unhandledProps = useUnhandledProps(PillContent.handledProps, props);
+
+ const element = (
+
+ {childrenExist(children) ? children : content}
+
+ );
+
+ setEnd();
+
+ return element;
+};
+
+PillContent.displayName = 'PillContent';
+
+PillContent.propTypes = {
+ ...commonPropTypes.createCommon(),
+};
+
+PillContent.handledProps = Object.keys(PillContent.propTypes) as any;
+
+PillContent.defaultProps = {
+ as: 'span',
+};
+
+PillContent.shorthandConfig = {
+ mappedProp: 'content',
+};
diff --git a/packages/fluentui/react-northstar/src/index.ts b/packages/fluentui/react-northstar/src/index.ts
index 04a0e51cdb8d98..e11721488301e2 100644
--- a/packages/fluentui/react-northstar/src/index.ts
+++ b/packages/fluentui/react-northstar/src/index.ts
@@ -72,6 +72,9 @@ export * from './components/Design/Design';
export * from './components/MenuButton/MenuButton';
+export * from './components/Pill/Pill';
+export * from './components/Pill/PillContent';
+
export * from './components/Divider/Divider';
export * from './components/Divider/DividerContent';
diff --git a/packages/fluentui/react-northstar/src/themes/teams/componentStyles.ts b/packages/fluentui/react-northstar/src/themes/teams/componentStyles.ts
index 239384c2e09641..5cd29e953e0c4a 100644
--- a/packages/fluentui/react-northstar/src/themes/teams/componentStyles.ts
+++ b/packages/fluentui/react-northstar/src/themes/teams/componentStyles.ts
@@ -97,6 +97,9 @@ export { menuDividerStyles as MenuDivider } from './components/Menu/menuDividerS
export { menuButtonStyles as MenuButton } from './components/MenuButton/menuButtonStyles';
+export { pillStyles as Pill } from './components/Pill/pillStyles';
+export { pillContentStyles as PillContent } from './components/Pill/pillContentStyles';
+
export { popupContentStyles as PopupContent } from './components/Popup/popupContentStyles';
export { providerStyles as Provider } from './components/Provider/providerStyles';
diff --git a/packages/fluentui/react-northstar/src/themes/teams/componentVariables.ts b/packages/fluentui/react-northstar/src/themes/teams/componentVariables.ts
index a1030b78c0b5cd..3d43639ce03374 100644
--- a/packages/fluentui/react-northstar/src/themes/teams/componentVariables.ts
+++ b/packages/fluentui/react-northstar/src/themes/teams/componentVariables.ts
@@ -90,6 +90,9 @@ export { menuItemContentVariables as MenuItemIndicator } from './components/Menu
export { menuItemWrapperVariables as MenuItemWrapper } from './components/Menu/menuItemWrapperVariables';
export { menuDividerVariables as MenuDivider } from './components/Menu/menuDividerVariables';
+export { pillVariables as Pill } from './components/Pill/pillVariables';
+export { pillVariables as PillContent } from './components/Pill/pillVariables';
+
export { popupContentVariables as PopupContent } from './components/Popup/popupContentVariables';
export { providerVariables as Provider } from './components/Provider/providerVariables';
diff --git a/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillContentStyles.ts b/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillContentStyles.ts
new file mode 100644
index 00000000000000..6ba101da950e8f
--- /dev/null
+++ b/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillContentStyles.ts
@@ -0,0 +1,19 @@
+import { ComponentSlotStylesPrepared, ICSSInJSStyle } from '@fluentui/styles';
+import { PillContentStylesProps } from '../../../../components/Pill/PillContent';
+import { PillVariables } from './pillVariables';
+
+export const pillContentStyles: ComponentSlotStylesPrepared = {
+ root: ({ props: p, variables: v }): ICSSInJSStyle => ({
+ fontSize: v.contentFontSize,
+ padding: v.contentPadding,
+
+ ...(p.size === 'small' && {
+ fontSize: v.contentFontSizeSmall,
+ padding: v.contentPaddingSmall,
+ }),
+ ...(p.size === 'smaller' && {
+ fontSize: v.contentFontSizeSmaller,
+ padding: v.contentPaddingSmaller,
+ }),
+ }),
+};
diff --git a/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillStyles.ts b/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillStyles.ts
new file mode 100644
index 00000000000000..b0f84b9b4284ed
--- /dev/null
+++ b/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillStyles.ts
@@ -0,0 +1,79 @@
+import { ComponentSlotStylesPrepared, ICSSInJSStyle } from '@fluentui/styles';
+import { PillStylesProps } from '../../../../components/Pill/Pill';
+import { PillVariables } from './pillVariables';
+import { getBorderFocusStyles } from '../../getBorderFocusStyles';
+
+export const pillStyles: ComponentSlotStylesPrepared = {
+ root: ({ props: p, variables: v, theme: { siteVariables } }): ICSSInJSStyle => {
+ return {
+ display: 'inline-flex',
+ width: 'fit-content',
+ height: v.height,
+ maxHeight: v.height,
+ borderRadius: v.borderRadius,
+ background: v.background,
+ margin: v.margin,
+ minWidth: v.minWidth,
+
+ ':hover': {
+ background: v.backgroundHover,
+ },
+
+ ...(p.rectangular && {
+ borderRadius: v.roundedBorderRadius,
+ ...((p.size === 'small' || p.size === 'smaller') && {
+ borderRadius: v.smallerRoundedBorderRadius,
+ }),
+ }),
+
+ ...(p.size === 'smaller' && {
+ minWidth: v.smallerMinWidth,
+ margin: v.smallerMargin,
+ height: v.smallerHeight,
+ maxHeight: v.smallerHeight,
+ }),
+
+ ...(p.size === 'small' && {
+ minWidth: v.smallMinWidth,
+ margin: v.smallMargin,
+ height: v.smallHeight,
+ maxHeight: v.smallHeight,
+ }),
+
+ ...(p.disabled && {
+ pointerEvents: 'none',
+ cursor: 'not-allowed',
+ background: v.disabledBackground,
+ color: v.disabledColor,
+ ':hover': {
+ background: v.disabledBackground,
+ },
+ }),
+
+ ...(p.appearance === 'outline' && {
+ borderWidth: '1px',
+ borderStyle: 'solid',
+ background: v.outlineBackground,
+ borderColor: v.outlineBorderColor,
+ ':hover': {
+ background: v.outlineBackground,
+ },
+ ...(p.disabled && {
+ borderColor: v.outlineDisabledborder,
+ }),
+ }),
+
+ ...(p.appearance === 'inverted' && {
+ background: v.invertedBackground,
+ ':hover': {
+ background: v.invertedBackground,
+ },
+ ...(p.disabled && {
+ background: v.disabledBackground,
+ }),
+ }),
+
+ ...getBorderFocusStyles({ variables: siteVariables }),
+ };
+ },
+};
diff --git a/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillVariables.ts b/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillVariables.ts
new file mode 100644
index 00000000000000..33da8f4396d1ce
--- /dev/null
+++ b/packages/fluentui/react-northstar/src/themes/teams/components/Pill/pillVariables.ts
@@ -0,0 +1,98 @@
+import { pxToRem } from '../../../../utils';
+import { SiteVariablesPrepared } from '@fluentui/styles';
+
+export interface PillVariables {
+ background: string;
+ backgroundHover: string;
+ borderRadius: string;
+ roundedBorderRadius: string;
+
+ // Outline
+ outlineBackground: string;
+ outlineBorderColor: string;
+ outlineDisabledborder: string;
+
+ // Inverted
+ invertedBackground: string;
+
+ // Disabled
+ disabledBackground: string;
+ disabledColor: string;
+
+ // Smaller
+ smallerHeight: string;
+ smallerMinWidth: string;
+ smallerMargin: string;
+
+ // Small
+ smallHeight: string;
+ smallMinWidth: string;
+ smallMargin: string;
+
+ // medium
+ height: string;
+ minWidth: string;
+ margin: string;
+ smallerRoundedBorderRadius: string;
+
+ // Content
+ contentPadding: string;
+ contentFontSize: string;
+
+ // Content Smaller
+ contentPaddingSmaller: string;
+ contentFontSizeSmaller: string;
+
+ // Content Small
+ contentPaddingSmall: string;
+ contentFontSizeSmall: string;
+}
+
+export const pillVariables = (siteVars: SiteVariablesPrepared): PillVariables => ({
+ background: siteVars.colorScheme.default.background3,
+ backgroundHover: siteVars.colorScheme.default.background1,
+ borderRadius: '9999px',
+ smallerRoundedBorderRadius: pxToRem(2),
+
+ // Disabled
+ disabledBackground: siteVars.colorScheme.default.backgroundDisabled,
+ disabledColor: siteVars.colorScheme.default.foregroundDisabled,
+
+ // Inverted
+ invertedBackground: siteVars.colorScheme.default.background,
+
+ // Outline
+ outlineBackground: 'transparent',
+ // TODO: The design spec maps to Neutral Stroke 1 that is equivalent to gre[440]
+ // but we don't have this token
+ outlineBorderColor: siteVars.colorScheme.default.borderActive4,
+ outlineDisabledborder: siteVars.colorScheme.default.borderDisabled,
+
+ // Smaller
+ smallerHeight: pxToRem(20),
+ smallerMinWidth: pxToRem(80),
+ smallerMargin: `${pxToRem(6)} ${pxToRem(2)}`,
+
+ // Small
+ smallHeight: pxToRem(24),
+ smallMinWidth: pxToRem(80),
+ smallMargin: pxToRem(4),
+
+ // Medium (default)
+ height: pxToRem(32),
+ minWidth: pxToRem(90),
+ margin: `${pxToRem(6)} ${pxToRem(4)}`,
+ roundedBorderRadius: pxToRem(4),
+
+ // Content Smaller
+ contentPaddingSmaller: `${pxToRem(2)} ${pxToRem(8)}`,
+ contentFontSizeSmaller: pxToRem(12),
+
+ // Content Small
+ contentFontSizeSmall: pxToRem(12),
+ contentPaddingSmall: `${pxToRem(4)} ${pxToRem(8)}`,
+
+ // Content Medium
+ contentFontSize: pxToRem(14),
+ contentPadding: `${pxToRem(6)} ${pxToRem(8)}`,
+});
diff --git a/packages/fluentui/react-northstar/src/themes/teams/types.ts b/packages/fluentui/react-northstar/src/themes/teams/types.ts
index 5f319cdcc74176..13252725c9b5bd 100644
--- a/packages/fluentui/react-northstar/src/themes/teams/types.ts
+++ b/packages/fluentui/react-northstar/src/themes/teams/types.ts
@@ -105,6 +105,8 @@ import { SkeletonAvatarStylesProps } from '../../components/Skeleton/SkeletonAva
import { SkeletonInputStylesProps } from '../../components/Skeleton/SkeletonInput';
import { SplitButtonToggleStylesProps } from '../../components/SplitButton/SplitButtonToggle';
import { AttachmentBodyStylesProps } from '../../components/Attachment/AttachmentBody';
+import { PillStylesProps } from '../../components/Pill/Pill';
+import { PillContentStylesProps } from '../../components/Pill/PillContent';
export type TeamsThemeStylesProps = {
Accordion: AccordionStylesProps;
@@ -162,6 +164,8 @@ export type TeamsThemeStylesProps = {
MenuItemContent: MenuItemContentStylesProps;
MenuItemWrapper: MenuItemWrapperStylesProps;
MenuDivider: MenuDividerStylesProps;
+ Pill: PillStylesProps;
+ PillContent: PillContentStylesProps;
Portal: PortalProps;
PopupContent: PopupContentStylesProps;
RadioGroup: RadioGroupProps;