diff --git a/packages/react-components/react-tags/etc/react-tags.api.md b/packages/react-components/react-tags/etc/react-tags.api.md index 5aa9b3b037832..eb354f54bba38 100644 --- a/packages/react-components/react-tags/etc/react-tags.api.md +++ b/packages/react-components/react-tags/etc/react-tags.api.md @@ -74,7 +74,7 @@ export type TagGroupState = ComponentState & Requ // @public export type TagProps = ComponentProps> & { - appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; + appearance?: 'filled' | 'outline' | 'brand'; disabled?: boolean; dismissible?: boolean; shape?: 'rounded' | 'circular'; diff --git a/packages/react-components/react-tags/src/components/InteractionTag/useInteractionTag.tsx b/packages/react-components/react-tags/src/components/InteractionTag/useInteractionTag.tsx index 193b60e030285..4da27da00f3e4 100644 --- a/packages/react-components/react-tags/src/components/InteractionTag/useInteractionTag.tsx +++ b/packages/react-components/react-tags/src/components/InteractionTag/useInteractionTag.tsx @@ -36,7 +36,7 @@ export const useInteractionTag_unstable = ( const id = useId('fui-Tag', props.id); const { - appearance = 'filled-lighter', + appearance = 'filled', disabled = false, dismissible = contextDismissible, shape = 'rounded', diff --git a/packages/react-components/react-tags/src/components/InteractionTag/useInteractionTagStyles.styles.ts b/packages/react-components/react-tags/src/components/InteractionTag/useInteractionTagStyles.styles.ts index b7dddee849adb..e94380c6c120c 100644 --- a/packages/react-components/react-tags/src/components/InteractionTag/useInteractionTagStyles.styles.ts +++ b/packages/react-components/react-tags/src/components/InteractionTag/useInteractionTagStyles.styles.ts @@ -30,10 +30,6 @@ const useRootStyles = makeStyles({ alignItems: 'center', boxSizing: 'border-box', width: 'fit-content', - - backgroundColor: tokens.colorNeutralBackground3, - color: tokens.colorNeutralForeground2, - ...shorthands.border(tokens.strokeWidthThin, 'solid', tokens.colorTransparentStroke), }, rounded: shorthands.borderRadius(tokens.borderRadiusMedium), @@ -71,27 +67,56 @@ const useContentButtonStyles = makeStyles({ "media secondary" `, + ...shorthands.border(tokens.strokeWidthThin, 'solid', tokens.colorTransparentStroke), + ...createCustomFocusIndicatorStyle({ + ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), + zIndex: 1, + }), + }, + + filled: { + backgroundColor: tokens.colorNeutralBackground3, + color: tokens.colorNeutralForeground2, ':hover': { cursor: 'pointer', + backgroundColor: tokens.colorNeutralBackground3Hover, + color: tokens.colorNeutralForeground2Hover, + }, + ':hover:active': { + backgroundColor: tokens.colorNeutralBackground3Pressed, + color: tokens.colorNeutralForeground2Pressed, }, }, - - rounded: createCustomFocusIndicatorStyle( - { - ...shorthands.borderRadius(tokens.borderRadiusMedium), - ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), - zIndex: 1, + outline: { + backgroundColor: tokens.colorSubtleBackground, + color: tokens.colorNeutralForeground2, + ...shorthands.borderColor(tokens.colorNeutralStroke1), + ':hover': { + cursor: 'pointer', + backgroundColor: tokens.colorSubtleBackgroundHover, + color: tokens.colorNeutralForeground2Hover, }, - { enableOutline: true }, - ), - circular: createCustomFocusIndicatorStyle( - { - ...shorthands.borderRadius(tokens.borderRadiusCircular), - ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), - zIndex: 1, + ':hover:active': { + backgroundColor: tokens.colorSubtleBackgroundPressed, + color: tokens.colorNeutralForeground2Pressed, }, - { enableOutline: true }, - ), + }, + brand: { + backgroundColor: tokens.colorBrandBackground2, + color: tokens.colorBrandForeground2, + ':hover': { + cursor: 'pointer', + backgroundColor: tokens.colorBrandBackground2Hover, + color: tokens.colorBrandForeground2Hover, + }, + ':hover:active': { + backgroundColor: tokens.colorBrandBackground2Pressed, + color: tokens.colorBrandForeground2Pressed, + }, + }, + + rounded: shorthands.borderRadius(tokens.borderRadiusMedium), + circular: shorthands.borderRadius(tokens.borderRadiusCircular), medium: { paddingRight: '7px', @@ -103,6 +128,27 @@ const useContentButtonStyles = makeStyles({ paddingRight: '5px', }, }); +const useContentButtonDisabledStyles = makeStyles({ + filled: { + cursor: 'not-allowed', + backgroundColor: tokens.colorNeutralBackgroundDisabled, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorTransparentStrokeDisabled), + }, + outline: { + cursor: 'not-allowed', + backgroundColor: tokens.colorSubtleBackground, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorNeutralStrokeDisabled), + }, + brand: { + cursor: 'not-allowed', + backgroundColor: tokens.colorNeutralBackgroundDisabled, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorTransparentStrokeDisabled), + }, +}); + /** * Styles for content slot when InteractionTag is without leading media/icon */ @@ -121,10 +167,15 @@ const useContentButtonWithoutMediaStyles = makeStyles({ * Styles for content slot when InteractionTag has dismiss button */ const useDismissibleContentButtonStyles = makeStyles({ - base: createCustomFocusIndicatorStyle({ + base: { borderTopRightRadius: tokens.borderRadiusNone, borderBottomRightRadius: tokens.borderRadiusNone, - }), + borderRightStyle: 'none', + ...createCustomFocusIndicatorStyle({ + borderTopRightRadius: tokens.borderRadiusNone, + borderBottomRightRadius: tokens.borderRadiusNone, + }), + }, medium: { paddingRight: tokens.spacingHorizontalS, }, @@ -151,30 +202,66 @@ const useDismissButtonStyles = makeStyles({ height: '100%', alignItems: 'center', + ...createCustomFocusIndicatorStyle(shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2)), + + ...shorthands.border(tokens.strokeWidthThin, 'solid', tokens.colorTransparentStroke), + // divider: - ...shorthands.borderLeft(tokens.strokeWidthThin, 'solid', tokens.colorNeutralStroke1), + borderLeftColor: tokens.colorNeutralStroke1, borderTopLeftRadius: tokens.borderRadiusNone, borderBottomLeftRadius: tokens.borderRadiusNone, + }, + filled: { + backgroundColor: tokens.colorNeutralBackground3, + color: tokens.colorNeutralForeground2, ':hover': { cursor: 'pointer', + backgroundColor: tokens.colorNeutralBackground3Hover, + color: tokens.colorNeutralForeground2BrandHover, + }, + ':hover:active': { + backgroundColor: tokens.colorNeutralBackground3Pressed, + color: tokens.colorNeutralForeground2BrandPressed, + }, + }, + outline: { + backgroundColor: tokens.colorSubtleBackground, + color: tokens.colorNeutralForeground2, + ...shorthands.borderColor(tokens.colorNeutralStroke1), + ':hover': { + cursor: 'pointer', + backgroundColor: tokens.colorSubtleBackgroundHover, + color: tokens.colorNeutralForeground2BrandHover, + }, + ':hover:active': { + backgroundColor: tokens.colorSubtleBackgroundPressed, + color: tokens.colorNeutralForeground2BrandPressed, + }, + }, + brand: { + backgroundColor: tokens.colorBrandBackground2, + color: tokens.colorBrandForeground2, + borderLeftColor: tokens.colorBrandStroke2, // divider + ':hover': { + cursor: 'pointer', + backgroundColor: tokens.colorBrandBackground2Hover, + color: tokens.colorBrandForeground2Hover, + }, + ':hover:active': { + backgroundColor: tokens.colorBrandBackground2Pressed, + color: tokens.colorBrandForeground2Pressed, }, }, - rounded: createCustomFocusIndicatorStyle({ - ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), - borderTopLeftRadius: tokens.borderRadiusNone, - borderBottomLeftRadius: tokens.borderRadiusNone, + rounded: { borderTopRightRadius: tokens.borderRadiusMedium, borderBottomRightRadius: tokens.borderRadiusMedium, - }), - circular: createCustomFocusIndicatorStyle({ - ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), - borderTopLeftRadius: tokens.borderRadiusNone, - borderBottomLeftRadius: tokens.borderRadiusNone, + }, + circular: { borderTopRightRadius: tokens.borderRadiusCircular, borderBottomRightRadius: tokens.borderRadiusCircular, - }), + }, medium: { fontSize: mediumIconSize, @@ -192,6 +279,28 @@ const useDismissButtonStyles = makeStyles({ paddingRight: '5px', }, }); +const useDismissButtonDisabledStyles = makeStyles({ + filled: { + cursor: 'not-allowed', + backgroundColor: tokens.colorNeutralBackgroundDisabled, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorTransparentStrokeDisabled), + borderLeftColor: tokens.colorNeutralStrokeDisabled, // divider + }, + outline: { + cursor: 'not-allowed', + backgroundColor: tokens.colorSubtleBackground, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorNeutralStrokeDisabled), + }, + brand: { + cursor: 'not-allowed', + backgroundColor: tokens.colorNeutralBackgroundDisabled, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorTransparentStrokeDisabled), + borderLeftColor: tokens.colorNeutralStrokeDisabled, // divider + }, +}); /** * Apply styling to the InteractionTag slots based on the state @@ -200,16 +309,19 @@ export const useInteractionTagStyles_unstable = (state: InteractionTagState): In const rootStyles = useRootStyles(); const contentButtonStyles = useContentButtonStyles(); + const contentButtonDisabledStyles = useContentButtonDisabledStyles(); const contentButtonWithoutMediaStyles = useContentButtonWithoutMediaStyles(); const dismissibleContentButtonStyles = useDismissibleContentButtonStyles(); + const dismissButtonStyles = useDismissButtonStyles(); + const dismissButtonDisabledStyles = useDismissButtonDisabledStyles(); + const iconStyles = useIconStyles(); const mediaStyles = useMediaStyles(); const primaryTextStyles = usePrimaryTextStyles(); const secondaryTextStyles = useSecondaryTextStyles(); - const dismissButtonStyles = useDismissButtonStyles(); - const { shape, size } = state; + const { shape, size, appearance } = state; state.root.className = mergeClasses( interactionTagClassNames.root, @@ -224,6 +336,7 @@ export const useInteractionTagStyles_unstable = (state: InteractionTagState): In interactionTagClassNames.content, contentButtonStyles.base, + state.disabled ? contentButtonDisabledStyles[appearance] : contentButtonStyles[appearance], contentButtonStyles[shape], contentButtonStyles[size], @@ -274,6 +387,7 @@ export const useInteractionTagStyles_unstable = (state: InteractionTagState): In state.dismissButton.className = mergeClasses( interactionTagClassNames.dismissButton, dismissButtonStyles.base, + state.disabled ? dismissButtonDisabledStyles[appearance] : dismissButtonStyles[appearance], dismissButtonStyles[shape], dismissButtonStyles[size], state.dismissButton.className, diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts index 2dd7710c60367..b5a32a318e1f0 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts +++ b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts @@ -37,7 +37,7 @@ export type TagSlots = { * Tag Props */ export type TagProps = ComponentProps> & { - appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; + appearance?: 'filled' | 'outline' | 'brand'; disabled?: boolean; dismissible?: boolean; shape?: 'rounded' | 'circular'; diff --git a/packages/react-components/react-tags/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/src/components/Tag/useTag.tsx index 006216ac2bf2a..36fdbd7146ce2 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -33,7 +33,7 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T const id = useId('fui-Tag', props.id); const { - appearance = 'filled-lighter', + appearance = 'filled', disabled = false, dismissible = contextDismissible, shape = 'rounded', diff --git a/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts b/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts index 66214279b8047..90a276b7bfb01 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts +++ b/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts @@ -42,34 +42,70 @@ const useRootStyles = makeStyles({ `, boxSizing: 'border-box', width: 'fit-content', - backgroundColor: tokens.colorNeutralBackground3, - color: tokens.colorNeutralForeground2, + ...shorthands.border(tokens.strokeWidthThin, 'solid', tokens.colorTransparentStroke), + }, + filled: { + backgroundColor: tokens.colorNeutralBackground3, + color: tokens.colorNeutralForeground2, ':hover': { cursor: 'pointer', + [`& .${tagClassNames.dismissIcon}`]: { + color: tokens.colorCompoundBrandForeground1Hover, + }, + }, + ':hover:active': { + [`& .${tagClassNames.dismissIcon}`]: { + color: tokens.colorCompoundBrandForeground1Pressed, + }, + }, + }, + outline: { + backgroundColor: tokens.colorSubtleBackground, + color: tokens.colorNeutralForeground2, + ...shorthands.borderColor(tokens.colorNeutralStroke1), + ':hover': { + cursor: 'pointer', + [`& .${tagClassNames.dismissIcon}`]: { + color: tokens.colorCompoundBrandForeground1Hover, + }, + }, + ':hover:active': { + [`& .${tagClassNames.dismissIcon}`]: { + color: tokens.colorCompoundBrandForeground1Pressed, + }, + }, + }, + brand: { + backgroundColor: tokens.colorBrandBackground2, + color: tokens.colorBrandForeground2, + ':hover': { + cursor: 'pointer', + [`& .${tagClassNames.dismissIcon}`]: { + color: tokens.colorCompoundBrandForeground1Hover, + }, + }, + ':hover:active': { + [`& .${tagClassNames.dismissIcon}`]: { + color: tokens.colorCompoundBrandForeground1Pressed, + }, }, }, rounded: { ...shorthands.borderRadius(tokens.borderRadiusMedium), - ...createCustomFocusIndicatorStyle( - { - ...shorthands.borderRadius(tokens.borderRadiusMedium), - ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), - }, - { enableOutline: true }, - ), + ...createCustomFocusIndicatorStyle({ + ...shorthands.borderRadius(tokens.borderRadiusMedium), + ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), + }), }, circular: { ...shorthands.borderRadius(tokens.borderRadiusCircular), - ...createCustomFocusIndicatorStyle( - { - ...shorthands.borderRadius(tokens.borderRadiusCircular), - ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), - }, - { enableOutline: true }, - ), + ...createCustomFocusIndicatorStyle({ + ...shorthands.borderRadius(tokens.borderRadiusCircular), + ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), + }), }, medium: { @@ -82,6 +118,28 @@ const useRootStyles = makeStyles({ height: '20px', }, }); + +const useRootDisabledStyles = makeStyles({ + filled: { + cursor: 'not-allowed', + backgroundColor: tokens.colorNeutralBackgroundDisabled, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorTransparentStrokeDisabled), + }, + outline: { + cursor: 'not-allowed', + backgroundColor: tokens.colorSubtleBackground, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorNeutralStrokeDisabled), + }, + brand: { + cursor: 'not-allowed', + backgroundColor: tokens.colorNeutralBackgroundDisabled, + color: tokens.colorNeutralForegroundDisabled, + ...shorthands.borderColor(tokens.colorTransparentStrokeDisabled), + }, +}); + /** * Styles for root slot when Tag is without leading media/icon */ @@ -220,6 +278,7 @@ export const useSecondaryTextStyles = makeStyles({ */ export const useTagStyles_unstable = (state: TagState): TagState => { const rootStyles = useRootStyles(); + const rootDisabledStyles = useRootDisabledStyles(); const rootWithoutMediaStyles = useRootWithoutMediaStyles(); const rootWithoutDismissStyles = useRootWithoutDismissStyles(); @@ -229,12 +288,14 @@ export const useTagStyles_unstable = (state: TagState): TagState => { const primaryTextStyles = usePrimaryTextStyles(); const secondaryTextStyles = useSecondaryTextStyles(); - const { shape, size } = state; + const { shape, size, appearance } = state; state.root.className = mergeClasses( tagClassNames.root, rootStyles.base, + + state.disabled ? rootDisabledStyles[appearance] : rootStyles[appearance], rootStyles[shape], rootStyles[size], diff --git a/packages/react-components/react-tags/stories/InteractionTag/InteractionTagAppearance.stories.tsx b/packages/react-components/react-tags/stories/InteractionTag/InteractionTagAppearance.stories.tsx new file mode 100644 index 0000000000000..d8f2e6c05289c --- /dev/null +++ b/packages/react-components/react-tags/stories/InteractionTag/InteractionTagAppearance.stories.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { makeStyles } from '@fluentui/react-components'; +import { InteractionTag } from '@fluentui/react-tags'; +import { bundleIcon, CalendarMonthFilled, CalendarMonthRegular } from '@fluentui/react-icons'; + +const CalendarMonth = bundleIcon(CalendarMonthFilled, CalendarMonthRegular); + +const useContainerStyles = makeStyles({ + container: { + columnGap: '10px', + display: 'flex', + }, +}); +export const Appearance = () => { + const styles = useContainerStyles(); + return ( +
+ } dismissible> + filled + + } dismissible> + outline + + } dismissible> + brand + +
+ ); +}; + +Appearance.storyName = 'Appearance'; +Appearance.parameters = { + docs: { + description: { + story: 'An InteractionTag can have a `filled`, `outline` or `brand` appearance. The default is `filled`.', + }, + }, +}; diff --git a/packages/react-components/react-tags/stories/InteractionTag/InteractionTagDisabled.stories.tsx b/packages/react-components/react-tags/stories/InteractionTag/InteractionTagDisabled.stories.tsx new file mode 100644 index 0000000000000..64e4876efe91b --- /dev/null +++ b/packages/react-components/react-tags/stories/InteractionTag/InteractionTagDisabled.stories.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { makeStyles } from '@fluentui/react-components'; +import { InteractionTag } from '@fluentui/react-tags'; +import { bundleIcon, CalendarMonthFilled, CalendarMonthRegular } from '@fluentui/react-icons'; + +const CalendarMonth = bundleIcon(CalendarMonthFilled, CalendarMonthRegular); + +const useContainerStyles = makeStyles({ + container: { + columnGap: '10px', + display: 'flex', + }, +}); +export const Disabled = () => { + const styles = useContainerStyles(); + return ( +
+ } dismissible> + disabled + + } + dismissible + > + disabled + + } dismissible> + disabled + +
+ ); +}; diff --git a/packages/react-components/react-tags/stories/InteractionTag/index.stories.tsx b/packages/react-components/react-tags/stories/InteractionTag/index.stories.tsx index a66cd8dbbf541..b253267a0812a 100644 --- a/packages/react-components/react-tags/stories/InteractionTag/index.stories.tsx +++ b/packages/react-components/react-tags/stories/InteractionTag/index.stories.tsx @@ -10,6 +10,8 @@ export { SecondaryText } from './InteractionTagSecondaryText.stories'; export { Dismiss } from './InteractionTagDismiss.stories'; export { Shape } from './InteractionTagShape.stories'; export { Size } from './InteractionTagSize.stories'; +export { Appearance } from './InteractionTagAppearance.stories'; +export { Disabled } from './InteractionTagDisabled.stories'; export default { title: 'Preview Components/Tag/InteractionTag', diff --git a/packages/react-components/react-tags/stories/Tag/TagAppearance.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagAppearance.stories.tsx new file mode 100644 index 0000000000000..6ef5d530f3e32 --- /dev/null +++ b/packages/react-components/react-tags/stories/Tag/TagAppearance.stories.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { makeStyles } from '@fluentui/react-components'; +import { Tag } from '@fluentui/react-tags'; +import { bundleIcon, CalendarMonthFilled, CalendarMonthRegular } from '@fluentui/react-icons'; + +const CalendarMonth = bundleIcon(CalendarMonthFilled, CalendarMonthRegular); + +const useContainerStyles = makeStyles({ + container: { + columnGap: '10px', + display: 'flex', + }, +}); +export const Appearance = () => { + const styles = useContainerStyles(); + return ( +
+ } dismissible> + filled + + } dismissible> + outline + + } dismissible> + brand + +
+ ); +}; + +Appearance.storyName = 'Appearance'; +Appearance.parameters = { + docs: { + description: { + story: 'A tag can have a `filled`, `outline` or `brand` appearance. The default is `filled`.', + }, + }, +}; diff --git a/packages/react-components/react-tags/stories/Tag/TagDisabled.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDisabled.stories.tsx new file mode 100644 index 0000000000000..f17b1446285f4 --- /dev/null +++ b/packages/react-components/react-tags/stories/Tag/TagDisabled.stories.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { makeStyles } from '@fluentui/react-components'; +import { Tag } from '@fluentui/react-tags'; +import { bundleIcon, CalendarMonthFilled, CalendarMonthRegular } from '@fluentui/react-icons'; + +const CalendarMonth = bundleIcon(CalendarMonthFilled, CalendarMonthRegular); + +const useContainerStyles = makeStyles({ + container: { + columnGap: '10px', + display: 'flex', + }, +}); +export const Disabled = () => { + const styles = useContainerStyles(); + return ( +
+ } dismissible> + disabled + + } dismissible> + disabled + + } dismissible> + disabled + +
+ ); +}; diff --git a/packages/react-components/react-tags/stories/Tag/index.stories.tsx b/packages/react-components/react-tags/stories/Tag/index.stories.tsx index b6b85183b5b5b..04fc2984760a3 100644 --- a/packages/react-components/react-tags/stories/Tag/index.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/index.stories.tsx @@ -10,6 +10,8 @@ export { SecondaryText } from './TagSecondaryText.stories'; export { Dismiss } from './TagDismiss.stories'; export { Shape } from './TagShape.stories'; export { Size } from './TagSize.stories'; +export { Appearance } from './TagAppearance.stories'; +export { Disabled } from './TagDisabled.stories'; export default { title: 'Preview Components/Tag/Tag',