diff --git a/example/src/Examples/TextInputExample.tsx b/example/src/Examples/TextInputExample.tsx index caecdc49b8..fbe11afd0a 100644 --- a/example/src/Examples/TextInputExample.tsx +++ b/example/src/Examples/TextInputExample.tsx @@ -672,7 +672,7 @@ const TextInputExample = () => { @@ -681,7 +681,7 @@ const TextInputExample = () => { diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 1cb2ca6bed..59cebe3a93 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -210,7 +210,7 @@ const Button = ( }, [mode] ); - const { roundness, animation } = theme; + const { animation } = theme; const uppercase = uppercaseProp ?? false; const isWeb = Platform.OS === 'web'; @@ -271,7 +271,7 @@ const Button = ( (style) => style.startsWith('border') && style.endsWith('Radius') ); - const borderRadius = 5 * roundness; + const borderRadius = theme.shapes.corner.largeIncreased; const iconSize = 18; const { diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 422101b4e8..c1522e5bf5 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -181,7 +181,7 @@ const Card = ( const { current: elevationDarkAdaptive } = React.useRef( new Animated.Value(cardElevation) ); - const { animation, dark, mode, roundness } = theme; + const { animation, dark, mode } = theme; const prevDarkRef = React.useRef(dark); React.useEffect(() => { @@ -267,7 +267,7 @@ const Card = ( ); const borderRadiusCombinedStyles = { - borderRadius: 3 * roundness, + borderRadius: theme.shapes.corner.medium, ...borderRadiusStyles, }; diff --git a/src/components/Card/utils.tsx b/src/components/Card/utils.tsx index b3d776662d..785a23c820 100644 --- a/src/components/Card/utils.tsx +++ b/src/components/Card/utils.tsx @@ -26,17 +26,15 @@ export const getCardCoverStyle = ({ index?: number; total?: number; }) => { - const { roundness } = theme; - if (Object.keys(borderRadiusStyles).length > 0) { return { - borderRadius: 3 * roundness, + borderRadius: theme.shapes.corner.medium, ...borderRadiusStyles, }; } return { - borderRadius: 3 * roundness, + borderRadius: theme.shapes.corner.medium, }; }; diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index fe3b30ab49..76d12d5ae1 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -205,7 +205,6 @@ const Chip = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { roundness } = theme; const isWeb = Platform.OS === 'web'; const { current: elevation } = React.useRef( @@ -244,7 +243,7 @@ const Chip = ({ }); const opacity = 0.38; - const defaultBorderRadius = roundness * 2; + const defaultBorderRadius = theme.shapes.corner.small; const iconSize = 18; const { diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index fd6597c34e..717445aed7 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -105,8 +105,7 @@ const Dialog = ({ }: Props) => { const { right, left } = useSafeAreaInsets(); const theme = useInternalTheme(themeOverrides); - const { roundness } = theme as Theme; - const borderRadius = 7 * roundness; + const borderRadius = (theme as Theme).shapes.corner.extraLarge; const backgroundColor = theme.colors.surfaceContainerHigh; diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 6902b4b8cb..9a68471474 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -100,7 +100,6 @@ const DrawerItem = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { roundness } = theme; const backgroundColor = active ? theme.colors.secondaryContainer : undefined; const contentColor = active @@ -108,7 +107,7 @@ const DrawerItem = ({ : theme.colors.onSurfaceVariant; const labelMargin = icon ? 12 : 0; - const borderRadius = 7 * roundness; + const borderRadius = theme.shapes.corner.extraLarge; const font = theme.fonts.labelLarge; return ( diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index ad49fa13be..93485aecb3 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -284,10 +284,10 @@ const v3LargeSize = { width: 96, }; -const getCustomFabSize = (customSize: number, roundness: number) => ({ +const getCustomFabSize = (customSize: number) => ({ height: customSize, width: customSize, - borderRadius: roundness === 0 ? 0 : customSize / roundness, + borderRadius: customSize / 4, }); export const getFabStyle = ({ @@ -299,17 +299,15 @@ export const getFabStyle = ({ size: 'small' | 'medium' | 'large'; theme: InternalTheme; }) => { - const { roundness } = theme; - - if (customSize) return getCustomFabSize(customSize, roundness); + if (customSize) return getCustomFabSize(customSize); switch (size) { case 'small': - return { ...v3SmallSize, borderRadius: 3 * roundness }; + return { ...v3SmallSize, borderRadius: theme.shapes.corner.medium }; case 'medium': - return { ...v3MediumSize, borderRadius: 4 * roundness }; + return { ...v3MediumSize, borderRadius: theme.shapes.corner.large }; case 'large': - return { ...v3LargeSize, borderRadius: 7 * roundness }; + return { ...v3LargeSize, borderRadius: theme.shapes.corner.extraLarge }; } }; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 4ba3d59a57..ebbd94e057 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -622,7 +622,7 @@ const Menu = ({ }), }, ], - borderRadius: theme.roundness, + borderRadius: theme.shapes.corner.extraSmall, ...(scrollableMenuHeight ? { height: scrollableMenuHeight } : {}), }; diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index 76c9e03218..69fbfb572d 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -20,6 +20,7 @@ import IconButton from './IconButton/IconButton'; import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; import { useInternalTheme } from '../core/theming'; +import { cornerNone } from '../theme/tokens/sys/shape'; import type { Theme, ThemeProp } from '../types'; import { forwardRef } from '../utils/forwardRef'; @@ -213,7 +214,7 @@ const Searchbar = forwardRef( onClearIconPress?.(e); }; - const { roundness, dark } = theme; + const { dark } = theme; const placeholderTextColor = theme.colors.onSurface; const textColor = theme.colors.onSurfaceVariant; @@ -237,10 +238,12 @@ const Searchbar = forwardRef( return ( { const isAndroid = Platform.OS === 'android'; - const { colors, roundness } = theme; + const { colors } = theme; + const roundness = theme.shapes.corner.extraSmall; const font = theme.fonts.bodyLarge; const hasActiveOutline = parentState.focused || error; @@ -156,8 +157,8 @@ const TextInputFlat = ({ const containerStyle = { backgroundColor, - borderTopLeftRadius: theme.roundness, - borderTopRightRadius: theme.roundness, + borderTopLeftRadius: roundness, + borderTopRightRadius: roundness, }; const labelScale = MINIMIZED_LABEL_FONT_SIZE / fontSize; diff --git a/src/components/TextInput/TextInputOutlined.tsx b/src/components/TextInput/TextInputOutlined.tsx index 63efb7f14b..aa563cf55f 100644 --- a/src/components/TextInput/TextInputOutlined.tsx +++ b/src/components/TextInput/TextInputOutlined.tsx @@ -81,7 +81,8 @@ const TextInputOutlined = ({ }: ChildTextInputProps) => { const adornmentConfig = getAdornmentConfig({ left, right }); - const { colors, roundness } = theme; + const { colors } = theme; + const roundness = theme.shapes.corner.extraSmall; const font = theme.fonts.bodyLarge; const hasActiveOutline = parentState.focused || error; diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index ba4bd138a3..71bc5bad9c 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -108,7 +108,7 @@ const ToggleButton = forwardRef( ref ) => { const theme = useInternalTheme(themeOverrides); - const borderRadius = theme.roundness; + const borderRadius = theme.shapes.corner.extraSmall; return ( diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index fdfef8571f..a3d199e879 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -209,7 +209,7 @@ const Tooltip = ({ measurement as Measurement, children as React.ReactElement ), - borderRadius: theme.roundness, + borderRadius: theme.shapes.corner.extraSmall, ...(measurement.measured ? styles.visible : styles.hidden), }, ]} diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index fb61ca9602..ac4eabfa11 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -220,7 +220,7 @@ describe('getCardCoverStyle - border radius', () => { theme: getTheme(), borderRadiusStyles: {}, }) - ).toMatchObject({ borderRadius: 3 * getTheme().roundness }); + ).toMatchObject({ borderRadius: getTheme().shapes.corner.medium }); }); }); diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 82841001ff..4ed5cad4ea 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -83,7 +83,7 @@ it('renders active chip if only onLongPress handler is passed', () => { it('renders chip with zero border radius', () => { const { getByTestId } = render( - + Active chip ); diff --git a/src/components/__tests__/FAB.test.tsx b/src/components/__tests__/FAB.test.tsx index 9fc9c12e98..91b54e22be 100644 --- a/src/components/__tests__/FAB.test.tsx +++ b/src/components/__tests__/FAB.test.tsx @@ -131,7 +131,12 @@ it('renders FAB with custom border radius', () => { it('renders FAB with zero border radius', () => { const { getByTestId } = render( - {}} icon="plus" testID="fab" /> + {}} + icon="plus" + testID="fab" + /> ); expect(getByTestId('fab-container')).toHaveStyle({ borderRadius: 0 }); diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index 80b6cc6c20..fe96dc80e6 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -187,7 +187,18 @@ exports[`renders list section with custom title style 1`] = ` "lineHeight": 20, }, }, - "roundness": 4, + "shapes": { + "corner": { + "extraExtraLarge": 48, + "extraLarge": 28, + "extraLargeIncreased": 32, + "extraSmall": 4, + "large": 16, + "largeIncreased": 20, + "medium": 12, + "small": 8, + }, + }, } } > @@ -729,7 +740,18 @@ exports[`renders list section with subheader 1`] = ` "lineHeight": 20, }, }, - "roundness": 4, + "shapes": { + "corner": { + "extraExtraLarge": 48, + "extraLarge": 28, + "extraLargeIncreased": 32, + "extraSmall": 4, + "large": 16, + "largeIncreased": 20, + "medium": 12, + "small": 8, + }, + }, } } > @@ -1269,7 +1291,18 @@ exports[`renders list section without subheader 1`] = ` "lineHeight": 20, }, }, - "roundness": 4, + "shapes": { + "corner": { + "extraExtraLarge": 48, + "extraLarge": 28, + "extraLargeIncreased": 32, + "extraSmall": 4, + "large": 16, + "largeIncreased": 20, + "medium": 12, + "small": 8, + }, + }, } } > diff --git a/src/theme/schemes/DarkTheme.tsx b/src/theme/schemes/DarkTheme.tsx index 85ca245e57..ab49b62400 100644 --- a/src/theme/schemes/DarkTheme.tsx +++ b/src/theme/schemes/DarkTheme.tsx @@ -1,6 +1,7 @@ import { baseTheme } from './base'; import { tokens } from '../tokens'; import { buildScheme } from '../tokens/sys/color/roles'; +import { defaultShapes } from '../tokens/sys/shape'; import type { Theme } from '../types'; export const DarkTheme: Theme = { @@ -8,4 +9,5 @@ export const DarkTheme: Theme = { dark: true, mode: 'adaptive', colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'dark' }), + shapes: defaultShapes, }; diff --git a/src/theme/schemes/LightTheme.tsx b/src/theme/schemes/LightTheme.tsx index d60f1a7709..b5d97a9cae 100644 --- a/src/theme/schemes/LightTheme.tsx +++ b/src/theme/schemes/LightTheme.tsx @@ -1,10 +1,12 @@ import { baseTheme } from './base'; import { tokens } from '../tokens'; import { buildScheme } from '../tokens/sys/color/roles'; +import { defaultShapes } from '../tokens/sys/shape'; import type { Theme } from '../types'; export const LightTheme: Theme = { ...baseTheme, dark: false, colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'light' }), + shapes: defaultShapes, }; diff --git a/src/theme/schemes/base.ts b/src/theme/schemes/base.ts index 68cf999cee..4601d7dc83 100644 --- a/src/theme/schemes/base.ts +++ b/src/theme/schemes/base.ts @@ -1,7 +1,6 @@ import configureFonts from '../fonts'; export const baseTheme = { - roundness: 4, fonts: configureFonts(), animation: { scale: 1.0, diff --git a/src/theme/tokens/sys/shape.ts b/src/theme/tokens/sys/shape.ts new file mode 100644 index 0000000000..6610004481 --- /dev/null +++ b/src/theme/tokens/sys/shape.ts @@ -0,0 +1,17 @@ +import type { ThemeShapes } from '../../types'; + +export const cornerNone = 0; +export const cornerFull = 9999; + +export const defaultShapes: ThemeShapes = { + corner: { + extraSmall: 4, + small: 8, + medium: 12, + large: 16, + largeIncreased: 20, + extraLarge: 28, + extraLargeIncreased: 32, + extraExtraLarge: 48, + }, +}; diff --git a/src/theme/types/index.ts b/src/theme/types/index.ts index 2207ed0526..e62ef4ad45 100644 --- a/src/theme/types/index.ts +++ b/src/theme/types/index.ts @@ -1,6 +1,7 @@ export * from './color'; export * from './elevation'; export * from './navigation'; +export * from './shape'; export * from './theme'; export * from './typography'; export * from './utils'; diff --git a/src/theme/types/shape.ts b/src/theme/types/shape.ts new file mode 100644 index 0000000000..78d16d2285 --- /dev/null +++ b/src/theme/types/shape.ts @@ -0,0 +1,14 @@ +export type ThemeShapeCorners = { + extraSmall: number; + small: number; + medium: number; + large: number; + largeIncreased: number; + extraLarge: number; + extraLargeIncreased: number; + extraExtraLarge: number; +}; + +export type ThemeShapes = { + corner: ThemeShapeCorners; +}; diff --git a/src/theme/types/theme.ts b/src/theme/types/theme.ts index 431b4ecfcf..dac2946cc9 100644 --- a/src/theme/types/theme.ts +++ b/src/theme/types/theme.ts @@ -1,6 +1,7 @@ import type { $DeepPartial } from '@callstack/react-theme-provider'; import type { ThemeColors } from './color'; +import type { ThemeShapes } from './shape'; import type { Typescale } from './typography'; type Mode = 'adaptive' | 'exact'; @@ -8,7 +9,6 @@ type Mode = 'adaptive' | 'exact'; export type ThemeBase = { dark: boolean; mode?: Mode; - roundness: number; animation: { scale: number; defaultAnimationDuration?: number; @@ -18,6 +18,7 @@ export type ThemeBase = { export type Theme = ThemeBase & { colors: ThemeColors; fonts: Typescale; + shapes: ThemeShapes; }; export type InternalTheme = Theme;