Skip to content

Commit

Permalink
[Material You] Add shape design tokens (#35393)
Browse files Browse the repository at this point in the history
  • Loading branch information
mnajdova committed Dec 13, 2022
1 parent 191acda commit e039510
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 31 deletions.
2 changes: 1 addition & 1 deletion docs/pages/experiments/md3/index.tsx
Expand Up @@ -159,7 +159,7 @@ function DemoComponents() {
bgcolor: 'error',
border: 1,
borderColor: 'tertiary',
borderRadius: '1px',
borderRadius: 'medium',
}}
>
Button
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-material-next/src/Button/Button.tsx
Expand Up @@ -268,7 +268,7 @@ export const ButtonRoot = styled('button', {
theme.sys.typescale.label.large.tracking / theme.sys.typescale.label.large.size
}rem`;

const borderRadiusValue: string | number = tokens.md3.shape.borderRadius;
const borderRadiusValue: string | number = tokens.sys.shape.corner.full;
const borderRadius = Number.isNaN(Number(borderRadiusValue))
? borderRadiusValue
: `${borderRadiusValue}px`;
Expand Down
40 changes: 25 additions & 15 deletions packages/mui-material-next/src/styles/Theme.types.ts
Expand Up @@ -89,7 +89,7 @@ export interface MD3Typeface {
};
}

export interface MD3States {
export interface MD3State {
hover: {
stateLayerOpacity: number;
};
Expand Down Expand Up @@ -129,21 +129,35 @@ export interface MD3Typescale {
display: TypescaleValue;
}

export interface Shapes {
borderRadius: number;
export interface MD3Shape {
corner: {
none: string;
extraSmall: string;
extraSmallTop: string;
small: string;
medium: string;
large: string;
largeEnd: string;
largeTop: string;
extraLarge: string;
extraLargeTop: string;
full: string;
};
}

export interface MD3ShapeOptions {
corner?: Partial<MD3Shape['corner']>;
}

export interface MD3CssVarsThemeOptions extends Omit<MD2CssVarsThemeOptions, 'colorSchemes'> {
md3?: {
shape?: Partial<Shapes>;
};
ref?: {
typeface?: Partial<MD3Typeface>;
};
sys?: {
typescale?: Partial<MD3Typescale>;
state?: Partial<MD3States>;
state?: Partial<MD3State>;
elevation?: string[];
shape?: MD3ShapeOptions;
};
}

Expand Down Expand Up @@ -173,11 +187,9 @@ export interface Theme extends Omit<MD2Theme, 'vars'> {
sys: {
color: MD3ColorSchemeTokens;
typescale: MD3Typescale;
state: MD3States;
state: MD3State;
elevation: string[];
};
md3: {
shape: Shapes;
shape: MD3Shape;
};
palette: MD2Theme['palette'];
vars: MD2Theme['vars'] & {
Expand All @@ -189,11 +201,9 @@ export interface Theme extends Omit<MD2Theme, 'vars'> {
sys: {
color: MD3ColorSchemeTokens;
typescale: MD3Typescale;
state: MD3States;
state: MD3State;
elevation: string[];
};
md3: {
shape: Shapes;
shape: MD3Shape;
};
};
unstable_sxConfig: SxConfig;
Expand Down
3 changes: 0 additions & 3 deletions packages/mui-material-next/src/styles/defaultTheme.ts
Expand Up @@ -12,7 +12,6 @@ export const getThemeWithVars = (
opacity,
overlays,
shape,
md3,
ref,
sys,
palette: paletteInput,
Expand All @@ -37,7 +36,6 @@ export const getThemeWithVars = (
opacity,
overlays,
shape,
md3,
ref: {
...ref,
...colorSchemeRef,
Expand Down Expand Up @@ -72,7 +70,6 @@ export const getThemeWithVars = (
...sys,
...colorSchemeSys,
},
md3,
palette,
},
} as unknown as Theme;
Expand Down
16 changes: 9 additions & 7 deletions packages/mui-material-next/src/styles/extendTheme.ts
Expand Up @@ -23,8 +23,9 @@ import createMd3LightColorScheme from './createLightColorScheme';
import createMd3DarkColorScheme from './createDarkColorScheme';
import md3Typescale from './typescale';
import md3Typeface from './typeface';
import md3State from './states';
import md3State from './state';
import { elevationLight, elevationDark } from './elevation';
import md3shape from './shape';

const defaultLightOverlays: Overlays = [...Array(25)].map(() => undefined) as Overlays;
const defaultDarkOverlays: Overlays = [...Array(25)].map((_, index) => {
Expand Down Expand Up @@ -55,6 +56,11 @@ export default function extendTheme(options: CssVarsThemeOptions = {}, ...args:

const md3LightColors = createMd3LightColorScheme(getCssVar, md3CommonPalette);
const md3DarkColors = createMd3DarkColorScheme(getCssVar, md3CommonPalette);
const shape = {
...input.sys?.shape,
...md3shape,
corner: { ...input.sys?.shape?.corner, ...md3shape.corner },
};

const {
palette: lightPalette,
Expand All @@ -79,12 +85,7 @@ export default function extendTheme(options: CssVarsThemeOptions = {}, ...args:
state: { ...md3State, ...input.sys?.state },
color: { ...md3LightColors, ...colorSchemesInput.light?.sys?.color },
elevation: colorSchemesInput.light?.sys?.elevation ?? elevationLight,
},
md3: {
shape: {
borderRadius: 100,
...input?.shape,
},
shape,
},
palette: {
...(colorSchemesInput.light && colorSchemesInput.light?.palette),
Expand Down Expand Up @@ -114,6 +115,7 @@ export default function extendTheme(options: CssVarsThemeOptions = {}, ...args:
state: { ...md3State, ...input.sys?.state },
color: { ...md3DarkColors, ...colorSchemesInput.dark?.sys?.color },
elevation: colorSchemesInput.dark?.sys?.elevation ?? elevationDark,
shape,
},
});

Expand Down
19 changes: 19 additions & 0 deletions packages/mui-material-next/src/styles/shape.ts
@@ -0,0 +1,19 @@
import { MD3Shape } from './Theme.types';

const mdSysShape: MD3Shape = {
corner: {
none: '0px',
extraSmall: '4px',
extraSmallTop: '4px 4px 0 0',
small: '8px',
medium: '12px',
large: '16px',
largeEnd: '0 16px 16px 0',
largeTop: '16px 16px 0 0',
extraLarge: '28px',
extraLargeTop: '28px 28px 0 0',
full: '100px',
},
};

export default mdSysShape;
@@ -1,4 +1,6 @@
const mdSysState = {
import { MD3State } from './Theme.types';

const mdSysState: MD3State = {
hover: {
stateLayerOpacity: 0.08,
},
Expand Down
26 changes: 26 additions & 0 deletions packages/mui-material-next/src/styles/styled.test.js
Expand Up @@ -198,5 +198,31 @@ describe('styled', () => {
borderRightColor: 'rgb(242, 184, 181)',
});
});

it('should apply borderRadius from sys.shape.corner', function test() {
if (/jsdom/.test(window.navigator.userAgent)) {
this.skip();
}
const Div = styled('div')``;

render(<Div sx={{ borderRadius: 'small' }} data-testid="target" />);

expect(screen.getByTestId('target')).toHaveComputedStyle({
borderTopLeftRadius: '8px',
});
});

it('should multiple borderRadius with theme.shape.borderRadius if provided as number', function test() {
if (/jsdom/.test(window.navigator.userAgent)) {
this.skip();
}
const Div = styled('div')``;

render(<Div sx={{ borderRadius: 4 }} data-testid="target" />);

expect(screen.getByTestId('target')).toHaveComputedStyle({
borderTopLeftRadius: '16px',
});
});
});
});
29 changes: 28 additions & 1 deletion packages/mui-material-next/src/styles/sxConfig.ts
@@ -1,4 +1,11 @@
import { getPath, handleBreakpoints, SxConfig, unstable_defaultSxConfig } from '@mui/system';
import {
getPath,
handleBreakpoints,
SxConfig,
unstable_defaultSxConfig,
createUnaryUnit,
getValue,
} from '@mui/system';

interface PaletteStyleOptions {
prop: string;
Expand Down Expand Up @@ -33,6 +40,23 @@ function createPaletteStyle(options: PaletteStyleOptions = { prop: 'color' }) {
return fn;
}

// eslint-disable-next-line no-restricted-globals
const isNumber = (value: string | number) => typeof value === 'number' || !isNaN(parseFloat(value));

const createBorderRadiusStyle = (props: Record<string, any>) => {
if (props.borderRadius !== undefined && props.borderRadius !== null) {
const numberTransformer = createUnaryUnit(props.theme, 'shape.borderRadius', 4, 'borderRadius');
const styleFromPropValue = (propValue: string | number) => ({
borderRadius: isNumber(propValue)
? getValue(numberTransformer, propValue)
: getPath(props.theme, `sys.shape.corner.${propValue}`, true),
});
return handleBreakpoints(props, props.borderRadius, styleFromPropValue);
}

return null;
};

const sxConfig: SxConfig = {
...unstable_defaultSxConfig,
color: {
Expand All @@ -59,6 +83,9 @@ const sxConfig: SxConfig = {
borderRightColor: {
style: createPaletteStyle({ prop: 'borderRightColor' }),
},
borderRadius: {
style: createBorderRadiusStyle,
},
};

export default sxConfig;
4 changes: 2 additions & 2 deletions packages/mui-system/src/spacing.d.ts
@@ -1,5 +1,6 @@
import { SimpleStyleFunction, spacing, PropsFor } from './Box';

export type SpacingValueType = string | number | null | undefined;
export type SpacingProps = PropsFor<typeof spacing>;
export function createUnarySpacing<Spacing>(theme: { spacing: Spacing }): Spacing extends number
? (abs: number | string) => number | number
Expand All @@ -16,7 +17,7 @@ export function createUnaryUnit<Spacing>(
defaultValue: Spacing,
propName: string,
): Spacing extends number
? (abs: number | string) => number | number
? (abs: SpacingValueType) => number | number
: Spacing extends any[]
? <Index extends number>(abs: Index | string) => Spacing[Index] | string
: Spacing extends (...args: unknown[]) => unknown
Expand Down Expand Up @@ -72,7 +73,6 @@ export const padding: SimpleStyleFunction<
| 'paddingBlockEnd'
>;

export type SpacingValueType = string | number | null | undefined;
export function getValue(
transformer: (prop: SpacingValueType) => SpacingValueType,
propValue: SpacingValueType,
Expand Down

0 comments on commit e039510

Please sign in to comment.