From 4e31e5c04d352f209af5be80ae9c8dc0d673de3d Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 5 May 2026 09:30:43 +0300 Subject: [PATCH 1/3] feat: add theme.state opacity tokens and split theme types --- .../__snapshots__/ListSection.test.tsx.snap | 24 +++ src/theme/schemes/DarkTheme.tsx | 2 + src/theme/schemes/DynamicTheme.android.tsx | 3 + src/theme/schemes/LightTheme.tsx | 2 + src/theme/tokens/index.ts | 4 +- src/theme/tokens/sys/state.ts | 13 ++ src/theme/types.ts | 179 ------------------ src/theme/types/color.ts | 59 ++++++ src/theme/types/elevation.ts | 14 ++ src/theme/types/index.ts | 7 + src/theme/types/navigation.ts | 11 ++ src/theme/types/state.ts | 5 + src/theme/types/theme.ts | 27 +++ src/theme/types/typography.ts | 61 ++++++ src/theme/types/utils.ts | 9 + 15 files changed, 239 insertions(+), 181 deletions(-) create mode 100644 src/theme/tokens/sys/state.ts delete mode 100644 src/theme/types.ts create mode 100644 src/theme/types/color.ts create mode 100644 src/theme/types/elevation.ts create mode 100644 src/theme/types/index.ts create mode 100644 src/theme/types/navigation.ts create mode 100644 src/theme/types/state.ts create mode 100644 src/theme/types/theme.ts create mode 100644 src/theme/types/typography.ts create mode 100644 src/theme/types/utils.ts diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index 80b6cc6c20..f5691bf2d2 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -188,6 +188,14 @@ exports[`renders list section with custom title style 1`] = ` }, }, "roundness": 4, + "state": { + "opacity": { + "dragged": 0.16, + "focused": 0.1, + "hovered": 0.08, + "pressed": 0.1, + }, + }, } } > @@ -730,6 +738,14 @@ exports[`renders list section with subheader 1`] = ` }, }, "roundness": 4, + "state": { + "opacity": { + "dragged": 0.16, + "focused": 0.1, + "hovered": 0.08, + "pressed": 0.1, + }, + }, } } > @@ -1270,6 +1286,14 @@ exports[`renders list section without subheader 1`] = ` }, }, "roundness": 4, + "state": { + "opacity": { + "dragged": 0.16, + "focused": 0.1, + "hovered": 0.08, + "pressed": 0.1, + }, + }, } } > diff --git a/src/theme/schemes/DarkTheme.tsx b/src/theme/schemes/DarkTheme.tsx index 85ca245e57..2bb6eacf5d 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 { defaultState } from '../tokens/sys/state'; 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' }), + state: defaultState, }; diff --git a/src/theme/schemes/DynamicTheme.android.tsx b/src/theme/schemes/DynamicTheme.android.tsx index 84667f5b79..4840210024 100644 --- a/src/theme/schemes/DynamicTheme.android.tsx +++ b/src/theme/schemes/DynamicTheme.android.tsx @@ -2,6 +2,7 @@ import { Platform, PlatformColor } from 'react-native'; import { DarkTheme } from './DarkTheme'; import { LightTheme } from './LightTheme'; +import { defaultState } from '../tokens/sys/state'; import type { Theme } from '../types'; const isApi34 = (Platform.Version as number) >= 34; @@ -494,9 +495,11 @@ const darkColors = { export const DynamicLightTheme: Theme = { ...LightTheme, colors: { ...LightTheme.colors, ...lightColors }, + state: defaultState, }; export const DynamicDarkTheme: Theme = { ...DarkTheme, colors: { ...DarkTheme.colors, ...darkColors }, + state: defaultState, }; diff --git a/src/theme/schemes/LightTheme.tsx b/src/theme/schemes/LightTheme.tsx index d60f1a7709..c63324fa63 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 { defaultState } from '../tokens/sys/state'; import type { Theme } from '../types'; export const LightTheme: Theme = { ...baseTheme, dark: false, colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'light' }), + state: defaultState, }; diff --git a/src/theme/tokens/index.ts b/src/theme/tokens/index.ts index ecc35ffa24..e8f041163e 100644 --- a/src/theme/tokens/index.ts +++ b/src/theme/tokens/index.ts @@ -122,8 +122,8 @@ const ref = { stateOpacity: { dragged: 0.16, pressed: 0.1, - focus: 0.1, - hover: 0.08, + focused: 0.1, + hovered: 0.08, disabled: 0.38, enabled: 1.0, }, diff --git a/src/theme/tokens/sys/state.ts b/src/theme/tokens/sys/state.ts new file mode 100644 index 0000000000..136564772b --- /dev/null +++ b/src/theme/tokens/sys/state.ts @@ -0,0 +1,13 @@ +import type { ThemeState } from '../../types'; +import { tokens } from '../index'; + +const { stateOpacity } = tokens.md.ref; + +export const defaultState: ThemeState = { + opacity: { + hovered: stateOpacity.hovered, + focused: stateOpacity.focused, + pressed: stateOpacity.pressed, + dragged: stateOpacity.dragged, + }, +}; diff --git a/src/theme/types.ts b/src/theme/types.ts deleted file mode 100644 index b424142b86..0000000000 --- a/src/theme/types.ts +++ /dev/null @@ -1,179 +0,0 @@ -import type * as React from 'react'; - -import type { $DeepPartial } from '@callstack/react-theme-provider'; - -export type Font = { - fontFamily: string; - fontWeight?: - | 'normal' - | 'bold' - | '100' - | '200' - | '300' - | '400' - | '500' - | '600' - | '700' - | '800' - | '900'; - fontStyle?: 'normal' | 'italic' | undefined; -}; - -export type Fonts = { - regular: Font; - medium: Font; - light: Font; - thin: Font; -}; - -type Mode = 'adaptive' | 'exact'; - -export type ThemeColors = { - primary: string; - primaryContainer: string; - secondary: string; - secondaryContainer: string; - tertiary: string; - tertiaryContainer: string; - surface: string; - surfaceDim: string; - surfaceBright: string; - surfaceContainerLowest: string; - surfaceContainerLow: string; - surfaceContainer: string; - surfaceContainerHigh: string; - surfaceContainerHighest: string; - surfaceVariant: string; - background: string; - error: string; - errorContainer: string; - onPrimary: string; - onPrimaryContainer: string; - onSecondary: string; - onSecondaryContainer: string; - onTertiary: string; - onTertiaryContainer: string; - onSurface: string; - onSurfaceVariant: string; - onError: string; - onErrorContainer: string; - onBackground: string; - outline: string; - outlineVariant: string; - inverseSurface: string; - inverseOnSurface: string; - inversePrimary: string; - primaryFixed: string; - primaryFixedDim: string; - onPrimaryFixed: string; - onPrimaryFixedVariant: string; - secondaryFixed: string; - secondaryFixedDim: string; - onSecondaryFixed: string; - onSecondaryFixedVariant: string; - tertiaryFixed: string; - tertiaryFixedDim: string; - onTertiaryFixed: string; - onTertiaryFixedVariant: string; - shadow: string; - scrim: string; - /** Pre-computed state layer color at press opacity (0.10). - * Used for ripple effects. Avoids runtime alpha manipulation - * which is incompatible with PlatformColor on Android. - * TODO: revisit after https://github.com/facebook/react-native/pull/56395 - * @see https://m3.material.io/foundations/interaction/states/state-layers */ - stateLayerPressed: string; - elevation: ElevationColors; -}; - -export type ThemeProp = $DeepPartial; - -export type ThemeBase = { - dark: boolean; - mode?: Mode; - roundness: number; - animation: { - scale: number; - defaultAnimationDuration?: number; - }; -}; - -export type Theme = ThemeBase & { - colors: ThemeColors; - fonts: Typescale; -}; - -export type InternalTheme = Theme; - -export enum TypescaleKey { - displayLarge = 'displayLarge', - displayMedium = 'displayMedium', - displaySmall = 'displaySmall', - - headlineLarge = 'headlineLarge', - headlineMedium = 'headlineMedium', - headlineSmall = 'headlineSmall', - - titleLarge = 'titleLarge', - titleMedium = 'titleMedium', - titleSmall = 'titleSmall', - - labelLarge = 'labelLarge', - labelMedium = 'labelMedium', - labelSmall = 'labelSmall', - - bodyLarge = 'bodyLarge', - bodyMedium = 'bodyMedium', - bodySmall = 'bodySmall', -} - -export type TypescaleStyle = { - fontFamily: string; - letterSpacing: number; - fontWeight: Font['fontWeight']; - lineHeight: number; - fontSize: number; - fontStyle?: Font['fontStyle']; -}; - -export type Typescale = - | { - [key in TypescaleKey]: TypescaleStyle; - } & { - ['default']: Omit; - }; - -export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; - -export enum ElevationLevels { - 'level0', - 'level1', - 'level2', - 'level3', - 'level4', - 'level5', -} - -export type ElevationColors = { - [key in keyof typeof ElevationLevels]: string; -}; - -export type $Omit = Pick>; -export type $RemoveChildren> = $Omit< - React.ComponentPropsWithoutRef, - 'children' ->; - -export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; - -export type NavigationTheme = { - dark: boolean; - colors: { - primary: string; - background: string; - card: string; - text: string; - border: string; - notification: string; - }; -}; diff --git a/src/theme/types/color.ts b/src/theme/types/color.ts new file mode 100644 index 0000000000..cc7ed49b86 --- /dev/null +++ b/src/theme/types/color.ts @@ -0,0 +1,59 @@ +import type { ElevationColors } from './elevation'; + +export type ThemeColors = { + primary: string; + primaryContainer: string; + secondary: string; + secondaryContainer: string; + tertiary: string; + tertiaryContainer: string; + surface: string; + surfaceDim: string; + surfaceBright: string; + surfaceContainerLowest: string; + surfaceContainerLow: string; + surfaceContainer: string; + surfaceContainerHigh: string; + surfaceContainerHighest: string; + surfaceVariant: string; + background: string; + error: string; + errorContainer: string; + onPrimary: string; + onPrimaryContainer: string; + onSecondary: string; + onSecondaryContainer: string; + onTertiary: string; + onTertiaryContainer: string; + onSurface: string; + onSurfaceVariant: string; + onError: string; + onErrorContainer: string; + onBackground: string; + outline: string; + outlineVariant: string; + inverseSurface: string; + inverseOnSurface: string; + inversePrimary: string; + primaryFixed: string; + primaryFixedDim: string; + onPrimaryFixed: string; + onPrimaryFixedVariant: string; + secondaryFixed: string; + secondaryFixedDim: string; + onSecondaryFixed: string; + onSecondaryFixedVariant: string; + tertiaryFixed: string; + tertiaryFixedDim: string; + onTertiaryFixed: string; + onTertiaryFixedVariant: string; + shadow: string; + scrim: string; + /** Pre-computed state layer color at press opacity (0.10). + * Used for ripple effects. Avoids runtime alpha manipulation + * which is incompatible with PlatformColor on Android. + * TODO: revisit after https://github.com/facebook/react-native/pull/56395 + * @see https://m3.material.io/foundations/interaction/states/state-layers */ + stateLayerPressed: string; + elevation: ElevationColors; +}; diff --git a/src/theme/types/elevation.ts b/src/theme/types/elevation.ts new file mode 100644 index 0000000000..9267a6c81b --- /dev/null +++ b/src/theme/types/elevation.ts @@ -0,0 +1,14 @@ +export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; + +export enum ElevationLevels { + 'level0', + 'level1', + 'level2', + 'level3', + 'level4', + 'level5', +} + +export type ElevationColors = { + [key in keyof typeof ElevationLevels]: string; +}; diff --git a/src/theme/types/index.ts b/src/theme/types/index.ts new file mode 100644 index 0000000000..562a7ba943 --- /dev/null +++ b/src/theme/types/index.ts @@ -0,0 +1,7 @@ +export * from './color'; +export * from './elevation'; +export * from './navigation'; +export * from './state'; +export * from './theme'; +export * from './typography'; +export * from './utils'; diff --git a/src/theme/types/navigation.ts b/src/theme/types/navigation.ts new file mode 100644 index 0000000000..51d4a6f763 --- /dev/null +++ b/src/theme/types/navigation.ts @@ -0,0 +1,11 @@ +export type NavigationTheme = { + dark: boolean; + colors: { + primary: string; + background: string; + card: string; + text: string; + border: string; + notification: string; + }; +}; diff --git a/src/theme/types/state.ts b/src/theme/types/state.ts new file mode 100644 index 0000000000..3ee801be9f --- /dev/null +++ b/src/theme/types/state.ts @@ -0,0 +1,5 @@ +export type InteractionState = 'hovered' | 'focused' | 'pressed' | 'dragged'; + +export type ThemeState = { + opacity: Record; +}; diff --git a/src/theme/types/theme.ts b/src/theme/types/theme.ts new file mode 100644 index 0000000000..742b3db5c4 --- /dev/null +++ b/src/theme/types/theme.ts @@ -0,0 +1,27 @@ +import type { $DeepPartial } from '@callstack/react-theme-provider'; + +import type { ThemeColors } from './color'; +import type { ThemeState } from './state'; +import type { Typescale } from './typography'; + +type Mode = 'adaptive' | 'exact'; + +export type ThemeBase = { + dark: boolean; + mode?: Mode; + roundness: number; + animation: { + scale: number; + defaultAnimationDuration?: number; + }; +}; + +export type Theme = ThemeBase & { + colors: ThemeColors; + fonts: Typescale; + state: ThemeState; +}; + +export type InternalTheme = Theme; + +export type ThemeProp = $DeepPartial; diff --git a/src/theme/types/typography.ts b/src/theme/types/typography.ts new file mode 100644 index 0000000000..96b34b0815 --- /dev/null +++ b/src/theme/types/typography.ts @@ -0,0 +1,61 @@ +export type Font = { + fontFamily: string; + fontWeight?: + | 'normal' + | 'bold' + | '100' + | '200' + | '300' + | '400' + | '500' + | '600' + | '700' + | '800' + | '900'; + fontStyle?: 'normal' | 'italic' | undefined; +}; + +export type Fonts = { + regular: Font; + medium: Font; + light: Font; + thin: Font; +}; + +export enum TypescaleKey { + displayLarge = 'displayLarge', + displayMedium = 'displayMedium', + displaySmall = 'displaySmall', + + headlineLarge = 'headlineLarge', + headlineMedium = 'headlineMedium', + headlineSmall = 'headlineSmall', + + titleLarge = 'titleLarge', + titleMedium = 'titleMedium', + titleSmall = 'titleSmall', + + labelLarge = 'labelLarge', + labelMedium = 'labelMedium', + labelSmall = 'labelSmall', + + bodyLarge = 'bodyLarge', + bodyMedium = 'bodyMedium', + bodySmall = 'bodySmall', +} + +export type TypescaleStyle = { + fontFamily: string; + letterSpacing: number; + fontWeight: Font['fontWeight']; + lineHeight: number; + fontSize: number; + fontStyle?: Font['fontStyle']; +}; + +export type Typescale = + | { + [key in TypescaleKey]: TypescaleStyle; + } & { + ['default']: Omit; + }; diff --git a/src/theme/types/utils.ts b/src/theme/types/utils.ts new file mode 100644 index 0000000000..0ea6b262b9 --- /dev/null +++ b/src/theme/types/utils.ts @@ -0,0 +1,9 @@ +import type * as React from 'react'; + +export type $Omit = Pick>; +export type $RemoveChildren> = $Omit< + React.ComponentPropsWithoutRef, + 'children' +>; + +export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; From 7f63048d5c37b8e436a0f4a213fd2a0e4165364c Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 5 May 2026 15:44:29 +0300 Subject: [PATCH 2/3] feat: add disabled and enabled opacity to theme.state --- .../__tests__/__snapshots__/ListSection.test.tsx.snap | 6 ++++++ src/theme/tokens/sys/state.ts | 2 ++ src/theme/types/state.ts | 8 +++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index f5691bf2d2..7149535a56 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -190,7 +190,9 @@ exports[`renders list section with custom title style 1`] = ` "roundness": 4, "state": { "opacity": { + "disabled": 0.38, "dragged": 0.16, + "enabled": 1, "focused": 0.1, "hovered": 0.08, "pressed": 0.1, @@ -740,7 +742,9 @@ exports[`renders list section with subheader 1`] = ` "roundness": 4, "state": { "opacity": { + "disabled": 0.38, "dragged": 0.16, + "enabled": 1, "focused": 0.1, "hovered": 0.08, "pressed": 0.1, @@ -1288,7 +1292,9 @@ exports[`renders list section without subheader 1`] = ` "roundness": 4, "state": { "opacity": { + "disabled": 0.38, "dragged": 0.16, + "enabled": 1, "focused": 0.1, "hovered": 0.08, "pressed": 0.1, diff --git a/src/theme/tokens/sys/state.ts b/src/theme/tokens/sys/state.ts index 136564772b..88dc40d041 100644 --- a/src/theme/tokens/sys/state.ts +++ b/src/theme/tokens/sys/state.ts @@ -9,5 +9,7 @@ export const defaultState: ThemeState = { focused: stateOpacity.focused, pressed: stateOpacity.pressed, dragged: stateOpacity.dragged, + disabled: stateOpacity.disabled, + enabled: stateOpacity.enabled, }, }; diff --git a/src/theme/types/state.ts b/src/theme/types/state.ts index 3ee801be9f..6b7ba307cb 100644 --- a/src/theme/types/state.ts +++ b/src/theme/types/state.ts @@ -1,4 +1,10 @@ -export type InteractionState = 'hovered' | 'focused' | 'pressed' | 'dragged'; +export type InteractionState = + | 'hovered' + | 'focused' + | 'pressed' + | 'dragged' + | 'disabled' + | 'enabled'; export type ThemeState = { opacity: Record; From 221faa2e57c0e9684598d947132805327c1b8379 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Mon, 11 May 2026 14:40:55 +0300 Subject: [PATCH 3/3] fix: code review findings --- src/components/Card/CardTitle.tsx | 4 +- src/components/Checkbox/CheckboxItem.tsx | 2 +- src/components/Menu/Menu.tsx | 12 +++-- .../RadioButton/RadioButtonItem.tsx | 2 +- src/components/Typography/types.tsx | 2 +- .../__snapshots__/ListSection.test.tsx.snap | 30 ------------ src/deprecated.ts | 5 +- src/theme/fonts.tsx | 8 +--- src/theme/schemes/DarkTheme.tsx | 2 - src/theme/schemes/DynamicTheme.android.tsx | 3 -- src/theme/schemes/LightTheme.tsx | 2 - src/theme/tokens/sys/state.ts | 15 ------ src/theme/types/elevation.ts | 13 +----- src/theme/types/index.ts | 1 - src/theme/types/state.ts | 11 ----- src/theme/types/theme.ts | 2 - src/theme/types/typography.ts | 46 ++++++++----------- 17 files changed, 36 insertions(+), 124 deletions(-) delete mode 100644 src/theme/tokens/sys/state.ts delete mode 100644 src/theme/types/state.ts diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index b37f903499..a8cda33fdc 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -40,7 +40,7 @@ export type Props = React.ComponentPropsWithRef & { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - titleVariant?: keyof typeof TypescaleKey; + titleVariant?: TypescaleKey; /** * Text for the subtitle. Note that this will only accept a string or ``-based node. */ @@ -69,7 +69,7 @@ export type Props = React.ComponentPropsWithRef & { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - subtitleVariant?: keyof typeof TypescaleKey; + subtitleVariant?: TypescaleKey; /** * Callback which returns a React element to display on the left side. */ diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 008f03640d..9037137e7a 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -86,7 +86,7 @@ export type Props = { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - labelVariant?: keyof typeof TypescaleKey; + labelVariant?: TypescaleKey; /** * @optional */ diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index a3f798d03a..4ba3d59a57 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -24,7 +24,6 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import MenuItem from './MenuItem'; import { useInternalTheme } from '../../core/theming'; import type { Elevation, Theme, ThemeProp } from '../../types'; -import { ElevationLevels } from '../../types'; import { addEventListener } from '../../utils/addEventListener'; import { BackHandler } from '../../utils/BackHandler/BackHandler'; import Portal from '../Portal/Portal'; @@ -105,9 +104,14 @@ const EASING = Easing.bezier(0.4, 0, 0.2, 1); const WINDOW_LAYOUT = Dimensions.get('window'); const DEFAULT_ELEVATION: Elevation = 2; -export const ELEVATION_LEVELS_MAP = Object.values( - ElevationLevels -) as ElevationLevels[]; +export const ELEVATION_LEVELS_MAP = [ + 'level0', + 'level1', + 'level2', + 'level3', + 'level4', + 'level5', +] as const; const DEFAULT_MODE = 'elevated'; diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index fe9b6843a8..936a2cdf3d 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -88,7 +88,7 @@ export type Props = { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - labelVariant?: keyof typeof TypescaleKey; + labelVariant?: TypescaleKey; /** * Specifies the largest possible scale a label font can reach. */ diff --git a/src/components/Typography/types.tsx b/src/components/Typography/types.tsx index 3f1754521a..ef7de9891a 100644 --- a/src/components/Typography/types.tsx +++ b/src/components/Typography/types.tsx @@ -2,4 +2,4 @@ import type { TypescaleKey } from '../../types'; export type VariantProp = | (T extends string ? (string extends T ? never : T) : never) - | keyof typeof TypescaleKey; + | TypescaleKey; diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index 7149535a56..80b6cc6c20 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -188,16 +188,6 @@ exports[`renders list section with custom title style 1`] = ` }, }, "roundness": 4, - "state": { - "opacity": { - "disabled": 0.38, - "dragged": 0.16, - "enabled": 1, - "focused": 0.1, - "hovered": 0.08, - "pressed": 0.1, - }, - }, } } > @@ -740,16 +730,6 @@ exports[`renders list section with subheader 1`] = ` }, }, "roundness": 4, - "state": { - "opacity": { - "disabled": 0.38, - "dragged": 0.16, - "enabled": 1, - "focused": 0.1, - "hovered": 0.08, - "pressed": 0.1, - }, - }, } } > @@ -1290,16 +1270,6 @@ exports[`renders list section without subheader 1`] = ` }, }, "roundness": 4, - "state": { - "opacity": { - "disabled": 0.38, - "dragged": 0.16, - "enabled": 1, - "focused": 0.1, - "hovered": 0.08, - "pressed": 0.1, - }, - }, } } > diff --git a/src/deprecated.ts b/src/deprecated.ts index e294f6a44d..ded0e3f10d 100644 --- a/src/deprecated.ts +++ b/src/deprecated.ts @@ -11,8 +11,7 @@ import { DarkTheme } from './theme/schemes/DarkTheme'; import { LightTheme } from './theme/schemes/LightTheme'; import { Palette } from './theme/tokens'; -import { TypescaleKey } from './types'; -import type { Theme, Elevation } from './types'; +import type { Theme, Elevation, TypescaleKey } from './types'; /** * @deprecated Use `LightTheme` instead. Will be removed in a future version. @@ -32,8 +31,6 @@ export const MD3Colors = Palette; /** * @deprecated Use `TypescaleKey` instead. Will be removed in a future version. */ -export const MD3TypescaleKey = TypescaleKey; -// eslint-disable-next-line no-redeclare export type MD3TypescaleKey = TypescaleKey; /** diff --git a/src/theme/fonts.tsx b/src/theme/fonts.tsx index 7808205474..e380655486 100644 --- a/src/theme/fonts.tsx +++ b/src/theme/fonts.tsx @@ -2,12 +2,8 @@ import { typescale } from './tokens'; import type { TypescaleStyle, Typescale, TypescaleKey } from './types'; type FontsConfig = - | { - [key in TypescaleKey]: Partial; - } - | { - [key: string]: TypescaleStyle; - } + | Record> + | Record | Partial; function configureFontsConfig( diff --git a/src/theme/schemes/DarkTheme.tsx b/src/theme/schemes/DarkTheme.tsx index 2bb6eacf5d..85ca245e57 100644 --- a/src/theme/schemes/DarkTheme.tsx +++ b/src/theme/schemes/DarkTheme.tsx @@ -1,7 +1,6 @@ import { baseTheme } from './base'; import { tokens } from '../tokens'; import { buildScheme } from '../tokens/sys/color/roles'; -import { defaultState } from '../tokens/sys/state'; import type { Theme } from '../types'; export const DarkTheme: Theme = { @@ -9,5 +8,4 @@ export const DarkTheme: Theme = { dark: true, mode: 'adaptive', colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'dark' }), - state: defaultState, }; diff --git a/src/theme/schemes/DynamicTheme.android.tsx b/src/theme/schemes/DynamicTheme.android.tsx index 4840210024..84667f5b79 100644 --- a/src/theme/schemes/DynamicTheme.android.tsx +++ b/src/theme/schemes/DynamicTheme.android.tsx @@ -2,7 +2,6 @@ import { Platform, PlatformColor } from 'react-native'; import { DarkTheme } from './DarkTheme'; import { LightTheme } from './LightTheme'; -import { defaultState } from '../tokens/sys/state'; import type { Theme } from '../types'; const isApi34 = (Platform.Version as number) >= 34; @@ -495,11 +494,9 @@ const darkColors = { export const DynamicLightTheme: Theme = { ...LightTheme, colors: { ...LightTheme.colors, ...lightColors }, - state: defaultState, }; export const DynamicDarkTheme: Theme = { ...DarkTheme, colors: { ...DarkTheme.colors, ...darkColors }, - state: defaultState, }; diff --git a/src/theme/schemes/LightTheme.tsx b/src/theme/schemes/LightTheme.tsx index c63324fa63..d60f1a7709 100644 --- a/src/theme/schemes/LightTheme.tsx +++ b/src/theme/schemes/LightTheme.tsx @@ -1,12 +1,10 @@ import { baseTheme } from './base'; import { tokens } from '../tokens'; import { buildScheme } from '../tokens/sys/color/roles'; -import { defaultState } from '../tokens/sys/state'; import type { Theme } from '../types'; export const LightTheme: Theme = { ...baseTheme, dark: false, colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'light' }), - state: defaultState, }; diff --git a/src/theme/tokens/sys/state.ts b/src/theme/tokens/sys/state.ts deleted file mode 100644 index 88dc40d041..0000000000 --- a/src/theme/tokens/sys/state.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { ThemeState } from '../../types'; -import { tokens } from '../index'; - -const { stateOpacity } = tokens.md.ref; - -export const defaultState: ThemeState = { - opacity: { - hovered: stateOpacity.hovered, - focused: stateOpacity.focused, - pressed: stateOpacity.pressed, - dragged: stateOpacity.dragged, - disabled: stateOpacity.disabled, - enabled: stateOpacity.enabled, - }, -}; diff --git a/src/theme/types/elevation.ts b/src/theme/types/elevation.ts index 9267a6c81b..839112f58c 100644 --- a/src/theme/types/elevation.ts +++ b/src/theme/types/elevation.ts @@ -1,14 +1,3 @@ export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; -export enum ElevationLevels { - 'level0', - 'level1', - 'level2', - 'level3', - 'level4', - 'level5', -} - -export type ElevationColors = { - [key in keyof typeof ElevationLevels]: string; -}; +export type ElevationColors = Record<`level${Elevation}`, string>; diff --git a/src/theme/types/index.ts b/src/theme/types/index.ts index 562a7ba943..2207ed0526 100644 --- a/src/theme/types/index.ts +++ b/src/theme/types/index.ts @@ -1,7 +1,6 @@ export * from './color'; export * from './elevation'; export * from './navigation'; -export * from './state'; export * from './theme'; export * from './typography'; export * from './utils'; diff --git a/src/theme/types/state.ts b/src/theme/types/state.ts deleted file mode 100644 index 6b7ba307cb..0000000000 --- a/src/theme/types/state.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type InteractionState = - | 'hovered' - | 'focused' - | 'pressed' - | 'dragged' - | 'disabled' - | 'enabled'; - -export type ThemeState = { - opacity: Record; -}; diff --git a/src/theme/types/theme.ts b/src/theme/types/theme.ts index 742b3db5c4..431b4ecfcf 100644 --- a/src/theme/types/theme.ts +++ b/src/theme/types/theme.ts @@ -1,7 +1,6 @@ import type { $DeepPartial } from '@callstack/react-theme-provider'; import type { ThemeColors } from './color'; -import type { ThemeState } from './state'; import type { Typescale } from './typography'; type Mode = 'adaptive' | 'exact'; @@ -19,7 +18,6 @@ export type ThemeBase = { export type Theme = ThemeBase & { colors: ThemeColors; fonts: Typescale; - state: ThemeState; }; export type InternalTheme = Theme; diff --git a/src/theme/types/typography.ts b/src/theme/types/typography.ts index 96b34b0815..57fcf3f67e 100644 --- a/src/theme/types/typography.ts +++ b/src/theme/types/typography.ts @@ -22,27 +22,22 @@ export type Fonts = { thin: Font; }; -export enum TypescaleKey { - displayLarge = 'displayLarge', - displayMedium = 'displayMedium', - displaySmall = 'displaySmall', - - headlineLarge = 'headlineLarge', - headlineMedium = 'headlineMedium', - headlineSmall = 'headlineSmall', - - titleLarge = 'titleLarge', - titleMedium = 'titleMedium', - titleSmall = 'titleSmall', - - labelLarge = 'labelLarge', - labelMedium = 'labelMedium', - labelSmall = 'labelSmall', - - bodyLarge = 'bodyLarge', - bodyMedium = 'bodyMedium', - bodySmall = 'bodySmall', -} +export type TypescaleKey = + | 'displayLarge' + | 'displayMedium' + | 'displaySmall' + | 'headlineLarge' + | 'headlineMedium' + | 'headlineSmall' + | 'titleLarge' + | 'titleMedium' + | 'titleSmall' + | 'labelLarge' + | 'labelMedium' + | 'labelSmall' + | 'bodyLarge' + | 'bodyMedium' + | 'bodySmall'; export type TypescaleStyle = { fontFamily: string; @@ -53,9 +48,6 @@ export type TypescaleStyle = { fontStyle?: Font['fontStyle']; }; -export type Typescale = - | { - [key in TypescaleKey]: TypescaleStyle; - } & { - ['default']: Omit; - }; +export type Typescale = Record & { + default: Omit; +};