Skip to content

Commit bf1f186

Browse files
terynkterynkumMitch-At-Work
committed
Field: Update to use Semantic Tokens (#34804)
Co-authored-by: terynkum <[email protected]> Co-authored-by: Mitch-At-Work <[email protected]>
1 parent 3651f3d commit bf1f186

File tree

14 files changed

+463
-18
lines changed

14 files changed

+463
-18
lines changed

packages/react-components/semantic-style-hooks-preview/library/etc/semantic-style-hooks-preview.api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { DividerState } from '@fluentui/react-divider';
2323
import type { DrawerBodyState } from '@fluentui/react-drawer';
2424
import { DrawerFooterState } from '@fluentui/react-drawer';
2525
import { DrawerHeaderState } from '@fluentui/react-drawer';
26+
import { FieldState } from '@fluentui/react-field';
2627
import { FlatTreeState } from '@fluentui/react-tree';
2728
import { FluentProviderCustomStyleHooks } from '@fluentui/react-provider';
2829
import { ImageState } from '@fluentui/react-image';
@@ -120,6 +121,9 @@ export const useSemanticDrawerFooterStyles: (_state: unknown) => DrawerFooterSta
120121
// @public
121122
export const useSemanticDrawerHeaderStyles: (_state: unknown) => DrawerHeaderState;
122123

124+
// @public
125+
export const useSemanticFieldStyles: (_state: unknown) => FieldState;
126+
123127
// @public (undocumented)
124128
export const useSemanticFlatTreeStyles: (_state: unknown) => FlatTreeState;
125129

packages/react-components/semantic-style-hooks-preview/library/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@fluentui/react-dialog": "^9.12.8",
3434
"@fluentui/react-divider": "^9.2.86",
3535
"@fluentui/react-drawer": "^9.7.8",
36+
"@fluentui/react-field": "^9.2.6",
3637
"@fluentui/react-icons": "^2.0.245",
3738
"@fluentui/react-image": "^9.1.84",
3839
"@fluentui/react-jsx-runtime": "^9.0.54",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { useSemanticFieldStyles } from './useSemanticFieldStyles.styles';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react';
2+
import { type FieldState, fieldClassNames } from '@fluentui/react-field';
3+
import { getSlotClassNameProp_unstable } from '@fluentui/react-utilities';
4+
import * as semanticTokens from '@fluentui/semantic-tokens';
5+
6+
// Size of the icon in the validation message
7+
const iconSize = '12px';
8+
9+
/**
10+
* Styles for the root slot
11+
*/
12+
const useRootStyles = makeStyles({
13+
base: {
14+
display: 'grid',
15+
},
16+
17+
// In horizontal layout, the field is a grid with the label taking up the entire first column.
18+
// The last row is slack space in case the label is taller than the rest of the content.
19+
horizontal: {
20+
gridTemplateColumns: '33% 1fr',
21+
gridTemplateRows: 'auto auto auto 1fr',
22+
},
23+
24+
// In horizontal layout without a label, replace the label's column with padding.
25+
// This lets grid auto-flow properly place the other children, and keeps fields with and without labels aligned.
26+
horizontalNoLabel: {
27+
paddingLeft: '33%',
28+
gridTemplateColumns: '1fr',
29+
},
30+
});
31+
32+
const useLabelStyles = makeStyles({
33+
vertical: {
34+
paddingTop: semanticTokens._ctrlFieldPaddingCtrlTextTop,
35+
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlTextBottom,
36+
marginBottom: semanticTokens.gapBetweenContentXSmall,
37+
},
38+
39+
verticalLarge: {
40+
paddingTop: semanticTokens._ctrlFieldPaddingCtrlLgTextTop,
41+
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlLgTextBottom,
42+
marginBottom: semanticTokens._ctrlFieldGapBetweenContentXSmall,
43+
},
44+
45+
horizontal: {
46+
paddingTop: semanticTokens._ctrlFieldPaddingCtrlTextTopHorizontal,
47+
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlTextBottomHorizontal,
48+
marginRight: semanticTokens._ctrlFieldPaddingCtrlTextSide,
49+
gridRowStart: '1',
50+
gridRowEnd: '-1',
51+
},
52+
53+
horizontalSmall: {
54+
paddingTop: semanticTokens._ctrlFieldPaddingCtrlSmTextTop,
55+
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlSmTextBottom,
56+
},
57+
58+
horizontalLarge: {
59+
// To align the label text with the Input text, it should be centered within the 40px height of the Input.
60+
// This is (40px - lineHeightBase400) / 2 = 9px. Hardcoded since there is no 9px padding token.
61+
paddingTop: semanticTokens._ctrlFieldPaddingCtrlLgTextTopHorizontal,
62+
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlLgTextBottomHorizontal,
63+
},
64+
});
65+
66+
const useSecondaryTextBaseClassName = makeResetStyles({
67+
marginTop: semanticTokens.gapBetweenContentXSmall,
68+
color: semanticTokens._ctrlFieldForegroundCtrlNeutralSecondaryRest,
69+
fontFamily: semanticTokens.textStyleDefaultRegularFontFamily,
70+
fontWeight: semanticTokens.textStyleDefaultRegularWeight,
71+
fontSize: semanticTokens.textRampMetadataFontSize,
72+
lineHeight: semanticTokens.textRampMetadataLineHeight,
73+
});
74+
75+
const useSecondaryTextStyles = makeStyles({
76+
error: {
77+
color: semanticTokens._ctrlFieldStatusDangerTintForeground,
78+
},
79+
80+
withIcon: {
81+
// Add a gutter for the icon, to allow multiple lines of text to line up to the right of the icon.
82+
paddingLeft: `calc(${iconSize} + ${semanticTokens.gapBetweenTextSmall})`,
83+
},
84+
});
85+
86+
const useValidationMessageIconBaseClassName = makeResetStyles({
87+
display: 'inline-block',
88+
fontSize: iconSize,
89+
// Negative left margin puts the icon in the gutter of the validation message div's withIcon style.
90+
marginLeft: `calc(-${iconSize} - ${semanticTokens.gapBetweenTextSmall})`,
91+
marginRight: semanticTokens.gapBetweenTextSmall,
92+
// Line height of 0 prevents the verticalAlign from affecting the line height of the text.
93+
lineHeight: '0',
94+
// Negative verticalAlign shifts the inline icon down to align with the text (effectively top padding).
95+
verticalAlign: '-1px',
96+
});
97+
98+
const useValidationMessageIconStyles = makeStyles({
99+
error: {
100+
color: semanticTokens._ctrlFieldStatusDangerTintForeground,
101+
},
102+
warning: {
103+
color: semanticTokens._ctrlFieldStatusWarningTintForeground,
104+
},
105+
success: {
106+
color: semanticTokens._ctrlFieldStatusSuccessTintForeground,
107+
},
108+
});
109+
110+
/**
111+
* Apply styling to the Field slots based on the state
112+
*/
113+
export const useSemanticFieldStyles = (_state: unknown): FieldState => {
114+
'use no memo';
115+
116+
const state = _state as FieldState;
117+
const { validationState, size } = state;
118+
const horizontal = state.orientation === 'horizontal';
119+
120+
const rootStyles = useRootStyles();
121+
state.root.className = mergeClasses(
122+
state.root.className,
123+
fieldClassNames.root,
124+
rootStyles.base,
125+
horizontal && rootStyles.horizontal,
126+
horizontal && !state.label && rootStyles.horizontalNoLabel,
127+
getSlotClassNameProp_unstable(state.root),
128+
);
129+
130+
const labelStyles = useLabelStyles();
131+
if (state.label) {
132+
state.label.className = mergeClasses(
133+
state.label.className,
134+
fieldClassNames.label,
135+
horizontal && labelStyles.horizontal,
136+
horizontal && size === 'small' && labelStyles.horizontalSmall,
137+
horizontal && size === 'large' && labelStyles.horizontalLarge,
138+
!horizontal && labelStyles.vertical,
139+
!horizontal && size === 'large' && labelStyles.verticalLarge,
140+
getSlotClassNameProp_unstable(state.label),
141+
);
142+
}
143+
144+
const validationMessageIconBaseClassName = useValidationMessageIconBaseClassName();
145+
const validationMessageIconStyles = useValidationMessageIconStyles();
146+
if (state.validationMessageIcon) {
147+
state.validationMessageIcon.className = mergeClasses(
148+
state.validationMessageIcon.className,
149+
fieldClassNames.validationMessageIcon,
150+
validationMessageIconBaseClassName,
151+
validationState !== 'none' && validationMessageIconStyles[validationState],
152+
getSlotClassNameProp_unstable(state.validationMessageIcon),
153+
);
154+
}
155+
156+
const secondaryTextBaseClassName = useSecondaryTextBaseClassName();
157+
const secondaryTextStyles = useSecondaryTextStyles();
158+
if (state.validationMessage) {
159+
state.validationMessage.className = mergeClasses(
160+
state.validationMessage.className,
161+
fieldClassNames.validationMessage,
162+
secondaryTextBaseClassName,
163+
validationState === 'error' && secondaryTextStyles.error,
164+
!!state.validationMessageIcon && secondaryTextStyles.withIcon,
165+
getSlotClassNameProp_unstable(state.validationMessage),
166+
);
167+
}
168+
169+
if (state.hint) {
170+
state.hint.className = mergeClasses(
171+
state.hint.className,
172+
fieldClassNames.hint,
173+
secondaryTextBaseClassName,
174+
getSlotClassNameProp_unstable(state.hint),
175+
);
176+
}
177+
178+
return state;
179+
};

packages/react-components/semantic-style-hooks-preview/library/src/component-styles/semanticStyleHooks.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
useSemanticDrawerHeaderStyles,
1919
useSemanticOverlayDrawerSurfaceStyles,
2020
} from './Drawer';
21+
import { useSemanticFieldStyles } from './Field';
2122
import { useSemanticLabelStyles } from './Label';
2223
import { useSemanticLinkStyles } from './Link';
2324
import { useSemanticProgressBarStyles } from './ProgressBar/useSemanticProgressBarStyles.styles';
@@ -91,6 +92,8 @@ export const SEMANTIC_STYLE_HOOKS: FluentProviderCustomStyleHooks = {
9192
useDrawerFooterStyles_unstable: useSemanticDrawerFooterStyles,
9293
useDrawerHeaderStyles_unstable: useSemanticDrawerHeaderStyles,
9394
useOverlayDrawerSurfaceStyles_unstable: useSemanticOverlayDrawerSurfaceStyles,
95+
//Field styles
96+
useFieldStyles_unstable: useSemanticFieldStyles,
9497
// Label styles
9598
useLabelStyles_unstable: useSemanticLabelStyles,
9699
// Link styles

packages/react-components/semantic-style-hooks-preview/library/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export {
1717
useSemanticDrawerHeaderStyles,
1818
useSemanticOverlayDrawerSurfaceStyles,
1919
} from './component-styles/Drawer';
20+
export { useSemanticFieldStyles } from './component-styles/Field';
2021
export { useSemanticLabelStyles } from './component-styles/Label';
2122
export { useSemanticLinkStyles } from './component-styles/Link';
2223
export { useSemanticProgressBarStyles } from './component-styles/ProgressBar/useSemanticProgressBarStyles.styles';

packages/semantic-tokens/etc/semantic-tokens.api.md

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,6 +1711,54 @@ export const ctrlFabShadowRest = "var(--smtc-ctrl-fab-shadow-rest)";
17111711
// @public (undocumented)
17121712
export const ctrlFabShadowRestRaw = "--smtc-ctrl-fab-shadow-rest";
17131713

1714+
// @public
1715+
export const _ctrlFieldForegroundCtrlNeutralSecondaryRest = "var(--smtc-foreground-ctrl-neutral-secondary-rest, var(--colorNeutralForeground3))";
1716+
1717+
// @public
1718+
export const _ctrlFieldGapBetweenContentXSmall = "var(--smtc-gap-between-content-x-small, var(--spacingVerticalXS))";
1719+
1720+
// @public
1721+
export const _ctrlFieldPaddingCtrlLgTextBottom = "var(--smtc-padding-ctrl-lg-text-bottom, var(--smtc-padding-ctrl-lg-text-top, 1px))";
1722+
1723+
// @public
1724+
export const _ctrlFieldPaddingCtrlLgTextBottomHorizontal = "var(--smtc-padding-ctrl-lg-text-bottom, var(--smtc-padding-ctrl-lg-text-top, 9px))";
1725+
1726+
// @public
1727+
export const _ctrlFieldPaddingCtrlLgTextTop = "var(--smtc-padding-ctrl-lg-text-top, 1px)";
1728+
1729+
// @public
1730+
export const _ctrlFieldPaddingCtrlLgTextTopHorizontal = "var(--smtc-padding-ctrl-lg-text-top, 9px)";
1731+
1732+
// @public
1733+
export const _ctrlFieldPaddingCtrlSmTextBottom = "var(--smtc-padding-ctrl-sm-text-bottom, var(--smtc-padding-ctrl-sm-text-top, var(--spacingVerticalXS)))";
1734+
1735+
// @public
1736+
export const _ctrlFieldPaddingCtrlSmTextTop = "var(--smtc-padding-ctrl-sm-text-top, var(--spacingVerticalXS))";
1737+
1738+
// @public
1739+
export const _ctrlFieldPaddingCtrlTextBottom = "var(--smtc-padding-ctrl-text-bottom, var(--smtc-padding-ctrl-text-top, var(--spacingVerticalXXS)))";
1740+
1741+
// @public
1742+
export const _ctrlFieldPaddingCtrlTextBottomHorizontal = "var(--smtc-padding-ctrl-text-bottom, var(--smtc-padding-ctrl-text-top, var(--spacingVerticalSNudge)))";
1743+
1744+
// @public
1745+
export const _ctrlFieldPaddingCtrlTextSide = "var(--smtc-padding-ctrl-text-side, var(--spacingHorizontalM))";
1746+
1747+
// @public
1748+
export const _ctrlFieldPaddingCtrlTextTop = "var(--smtc-padding-ctrl-text-top, var(--spacingVerticalXXS))";
1749+
1750+
// @public
1751+
export const _ctrlFieldPaddingCtrlTextTopHorizontal = "var(--smtc-padding-ctrl-text-top, var(--spacingVerticalSNudge))";
1752+
1753+
// @public
1754+
export const _ctrlFieldStatusDangerTintForeground = "var(--smtc-status-danger-tint-foreground, var(--colorPaletteRedForeground1))";
1755+
1756+
// @public
1757+
export const _ctrlFieldStatusSuccessTintForeground = "var(--smtc-status-success-tint-foreground, var(--colorPaletteGreenForeground1))";
1758+
1759+
// @public
1760+
export const _ctrlFieldStatusWarningTintForeground = "var(--smtc-status-warning-tint-foreground, var(--colorPaletteDarkOrangeForeground1))";
1761+
17141762
// @public (undocumented)
17151763
export const ctrlFocusInnerStroke = "var(--smtc-ctrl-focus-inner-stroke, var(--colorStrokeFocus2))";
17161764

@@ -3545,7 +3593,7 @@ export const gapBetweenContentXLarge = "var(--smtc-gap-between-content-x-large)"
35453593
export const gapBetweenContentXLargeRaw = "--smtc-gap-between-content-x-large";
35463594

35473595
// @public (undocumented)
3548-
export const gapBetweenContentXSmall = "var(--smtc-gap-between-content-x-small)";
3596+
export const gapBetweenContentXSmall = "var(--smtc-gap-between-content-x-small, var(--spacingVerticalXXS))";
35493597

35503598
// @public (undocumented)
35513599
export const gapBetweenContentXSmallRaw = "--smtc-gap-between-content-x-small";
@@ -3611,7 +3659,7 @@ export const gapBetweenTextLarge = "var(--smtc-gap-between-text-large, var(--smt
36113659
export const gapBetweenTextLargeRaw = "--smtc-gap-between-text-large";
36123660

36133661
// @public (undocumented)
3614-
export const gapBetweenTextSmall = "var(--smtc-gap-between-text-small, var(--smtc-gap-between-content-xx-small))";
3662+
export const gapBetweenTextSmall = "var(--smtc-gap-between-text-small, var(--smtc-gap-between-content-xx-small, var(--spacingHorizontalXS)))";
36153663

36163664
// @public (undocumented)
36173665
export const gapBetweenTextSmallRaw = "--smtc-gap-between-text-small";
@@ -5695,7 +5743,7 @@ export const textRampMetadataFontSize = "var(--smtc-text-ramp-metadata-font-size
56955743
export const textRampMetadataFontSizeRaw = "--smtc-text-ramp-metadata-font-size";
56965744

56975745
// @public (undocumented)
5698-
export const textRampMetadataLineHeight = "var(--smtc-text-ramp-metadata-line-height, var(--smtc-text-global-caption1-line-height))";
5746+
export const textRampMetadataLineHeight = "var(--smtc-text-ramp-metadata-line-height, var(--smtc-text-global-caption1-line-height, var(--lineHeightBase200)))";
56995747

57005748
// @public (undocumented)
57015749
export const textRampMetadataLineHeightRaw = "--smtc-text-ramp-metadata-line-height";

packages/semantic-tokens/src/control/tokens.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
spacingHorizontalXXL,
3030
spacingHorizontalS,
3131
spacingHorizontalL,
32+
spacingVerticalXXS,
3233
spacingHorizontalXS,
3334
spacingHorizontalSNudge,
3435
borderRadiusCircular,
@@ -343,7 +344,7 @@ export const paddingCtrlLgTextTop = `var(${paddingCtrlLgTextTopRaw})`;
343344
export const paddingCtrlLgToNestedControl = `var(${paddingCtrlLgToNestedControlRaw})`;
344345
export const gapBetweenContentNone = `var(${gapBetweenContentNoneRaw})`;
345346
export const gapBetweenContentXxSmall = `var(${gapBetweenContentXxSmallRaw})`;
346-
export const gapBetweenContentXSmall = `var(${gapBetweenContentXSmallRaw})`;
347+
export const gapBetweenContentXSmall = `var(${gapBetweenContentXSmallRaw}, ${spacingVerticalXXS})`;
347348
export const gapBetweenContentSmall = `var(${gapBetweenContentSmallRaw}, 8px)`;
348349
export const gapBetweenCtrlDefault = `var(${gapBetweenCtrlDefaultRaw}, 8px)`;
349350
export const gapBetweenContentMedium = `var(${gapBetweenContentMediumRaw})`;

0 commit comments

Comments
 (0)