From c75bb8412ff58739d33cd476ab6faf1bdc4ebda3 Mon Sep 17 00:00:00 2001 From: Chris Glein <26607885+chrisglein@users.noreply.github.com> Date: Tue, 30 Sep 2025 18:15:01 -0700 Subject: [PATCH 1/2] Create new page for showing how to use PlatformColor and for help finding valid PlatformColor values --- NewArch/src/RNGalleryList.ts | 9 + .../src/examples/PlatformColorExamplePage.tsx | 225 ++++++++++++++++++ src/RNGalleryList.ts | 9 + src/examples/PlatformColorExamplePage.tsx | 218 +++++++++++++++++ 4 files changed, 461 insertions(+) create mode 100644 NewArch/src/examples/PlatformColorExamplePage.tsx create mode 100644 src/examples/PlatformColorExamplePage.tsx diff --git a/NewArch/src/RNGalleryList.ts b/NewArch/src/RNGalleryList.ts index b8995674..450d8533 100644 --- a/NewArch/src/RNGalleryList.ts +++ b/NewArch/src/RNGalleryList.ts @@ -41,6 +41,7 @@ import { ModalExamplePage } from './examples/ModalExamplePage'; // Fabric only import {VirtualizedListExamplePage} from './examples/VirtualizedListExamplePage'; // import {LinearGradientExamplePage} from './examples/LinearGradientExamplePage'; // import {NetworkExamplePage} from './examples/NetworkExamplePage'; +import {PlatformColorExamplePage} from './examples/PlatformColorExamplePage'; // import {SvgExamplePage} from './examples/SvgExamplePage'; // import {LottieAnimationsExamplePage} from './examples/LottieAnimationsExamplePage'; @@ -207,6 +208,14 @@ export const RNGalleryList: Array = [ // subtitle: 'Displays content on top of existing content.', // type: 'Dialogs & flyouts', // }, + { + key: 'PlatformColor', + component: PlatformColorExamplePage, + textIcon: '\uE790', + subtitle: 'Reference platform-specific colors that adapt to user themes and accessibility settings.', + type: 'System', + new: true, + }, { key: 'Pressable', component: PressableExamplePage, diff --git a/NewArch/src/examples/PlatformColorExamplePage.tsx b/NewArch/src/examples/PlatformColorExamplePage.tsx new file mode 100644 index 00000000..4e44e310 --- /dev/null +++ b/NewArch/src/examples/PlatformColorExamplePage.tsx @@ -0,0 +1,225 @@ +'use strict'; +import {View, Text, PlatformColor, ScrollView, Pressable, StyleSheet} from 'react-native'; +import React, { useState } from 'react'; +import {Example} from '../components/Example'; +import {Page} from '../components/Page'; +import {useTheme} from '@react-navigation/native'; + + +function PlatformColorVisualizer({ colorKey }: { colorKey: string }) { + const [darkText, setDarkText] = useState(false); + return ( + setDarkText((prev) => !prev)}> + + {colorKey} + + + ); +} + +// Source for these is here: https://github.com/microsoft/react-native-windows/blob/63e9eaaa2249c3f7b46200203cb69c006c5770a9/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.cpp +// Grouping done in alignment with WinUI Gallery's Design->Color section +const platformColorGroups: { header: string; colors: string[] }[] = [ + { + header: 'Control Fill', + colors: [ + 'ControlFillColorDefault', + 'ControlFillColorSecondary', + 'ControlFillColorTertiary', + 'ControlFillColorDisabled', + 'ControlFillColorTransparent', + ], + }, + { + header: 'Control Alt Fill', + colors: [ + 'ControlAltFillColorSecondary', + 'ControlAltFillColorTertiary', + 'ControlAltFillColorQuarternary', + 'ControlAltFillColorDisabled', + ], + }, + { + header: 'Strong Fill', + colors: [ + 'ControlStrongFillColorDefault', + 'ControlStrongFillColorDisabled', + ], + }, + { + header: 'Subtle Fill', + colors: [ + 'SubtleFillColorTransparent', + 'SubtleFillColorSecondary', + ], + }, + { + header: 'Accent Fill', + colors: [ + 'AccentFillColorDefault', + 'AccentFillColorSecondary', + 'AccentFillColorTertiary', + 'AccentFillColorDisabled', + ], + }, + { + header: 'Text', + colors: [ + 'TextFillColorPrimary', + 'TextFillColorSecondary', + 'TextFillColorDisabled', + ], + }, + { + header: 'Text On Accent', + colors: [ + 'TextOnAccentFillColorPrimary', + 'TextOnAccentFillColorDisabled', + ], + }, + { + header: 'Control Stroke', + colors: [ + 'ControlStrokeColorDefault', + 'ControlStrokeColorSecondary', + 'ControlStrokeColorOnAccentSecondary', + 'ControlStrongStrokeColorDefault', + 'ControlStrongStrokeColorDisabled', + ], + }, + { + header: 'Other', + colors: [ + 'SolidBackgroundFillColorBase', + 'CircleElevationBorder', + 'ProgressRingForegroundTheme', + 'TextControlForeground', + 'ScrollBarButtonBackground', + 'ScrollBarButtonBackgroundPointerOver', + 'ScrollBarButtonBackgroundPressed', + 'ScrollBarButtonBackgroundDisabled', + 'ScrollBarButtonArrowForeground', + 'ScrollBarButtonArrowForegroundPointerOver', + 'ScrollBarButtonArrowForegroundPressed', + 'ScrollBarButtonArrowForegroundDisabled', + 'ScrollBarThumbFill', + 'ScrollBarThumbFillPointerOver', + 'ScrollBarThumbFillPressed', + 'ScrollBarThumbFillDisabled', + 'ScrollBarTrackFill', + 'ToolTipBackground', + 'ToolTipForeground', + 'ToolTipBorderBrush', + 'SystemChromeMediumLowColor', + 'SystemControlForegroundBaseHighColor', + 'SystemControlTransientBorderColor', + 'FocusVisualPrimary', + 'FocusVisualSecondary', + ], + }, +]; + + +function PlatformColorList() { + const scrollChildren = []; + const stickyHeaderIndices = []; + let childIndex = 0; + for (const group of platformColorGroups) { + stickyHeaderIndices.push(childIndex); + scrollChildren.push( + {group.header} + ); + childIndex++; + for (const colorKey of group.colors) { + scrollChildren.push( + + ); + childIndex++; + } + } + + return ( + + {scrollChildren} + + ); +} + +export const PlatformColorExamplePage: React.FunctionComponent<{}> = () => { + const example1jsx = ` + + System Accent Color + +`; + + return ( + + + + + System Accent Color + + + + + + + + + ); +}; + + +const styles = StyleSheet.create({ + scrollContent: { + gap: 8, + alignItems: 'flex-start', + }, + sectionHeader: { + fontWeight: '600', + fontSize: 16, + marginTop: 16, + marginBottom: 4, + }, + colorText: { + paddingTop: 6, + paddingBottom: 4, + paddingHorizontal: 14, + borderRadius: 6, + color: '#fff', + fontSize: 15, + overflow: 'hidden', + }, +}); diff --git a/src/RNGalleryList.ts b/src/RNGalleryList.ts index 97e55fa8..f2d3d223 100644 --- a/src/RNGalleryList.ts +++ b/src/RNGalleryList.ts @@ -40,6 +40,7 @@ import {ExpanderExamplePage} from './examples/ExpanderExamplePage'; import {VirtualizedListExamplePage} from './examples/VirtualizedListExamplePage'; import {LinearGradientExamplePage} from './examples/LinearGradientExamplePage'; import {NetworkExamplePage} from './examples/NetworkExamplePage'; +import {PlatformColorExamplePage} from './examples/PlatformColorExamplePage'; import {SvgExamplePage} from './examples/SvgExamplePage'; import {LottieAnimationsExamplePage} from './examples/LottieAnimationsExamplePage'; @@ -197,6 +198,14 @@ export const RNGalleryList: Array = [ subtitle: 'A drop-down list of items a user can select from.', type: 'Basic Input', }, + { + key: 'PlatformColor', + component: PlatformColorExamplePage, + textIcon: '\uE790', + subtitle: 'Reference platform-specific colors that adapt to user themes and accessibility settings.', + type: 'System', + new: true, + }, { key: 'Popup', component: PopupExamplePage, diff --git a/src/examples/PlatformColorExamplePage.tsx b/src/examples/PlatformColorExamplePage.tsx new file mode 100644 index 00000000..dd4b6b56 --- /dev/null +++ b/src/examples/PlatformColorExamplePage.tsx @@ -0,0 +1,218 @@ +'use strict'; +import {View, Text, PlatformColor, ScrollView, Pressable, StyleSheet} from 'react-native'; +import React, { useState } from 'react'; +import {Example} from '../components/Example'; +import {Page} from '../components/Page'; + + +function PlatformColorVisualizer({ colorKey }: { colorKey: string }) { + const [darkText, setDarkText] = useState(false); + return ( + setDarkText((prev) => !prev)}> + + {colorKey} + + + ); +} + +// Source for these is here: https://github.com/microsoft/react-native-windows/blob/63e9eaaa2249c3f7b46200203cb69c006c5770a9/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.cpp +// Grouping done in alignment with WinUI Gallery's Design->Color section +const platformColorGroups: { header: string; colors: string[] }[] = [ + { + header: 'Control Fill', + colors: [ + 'ControlFillColorDefault', + 'ControlFillColorSecondary', + 'ControlFillColorTertiary', + 'ControlFillColorDisabled', + 'ControlFillColorTransparent', + ], + }, + { + header: 'Control Alt Fill', + colors: [ + 'ControlAltFillColorSecondary', + 'ControlAltFillColorTertiary', + 'ControlAltFillColorQuarternary', + 'ControlAltFillColorDisabled', + ], + }, + { + header: 'Strong Fill', + colors: [ + 'ControlStrongFillColorDefault', + 'ControlStrongFillColorDisabled', + ], + }, + { + header: 'Subtle Fill', + colors: [ + 'SubtleFillColorTransparent', + 'SubtleFillColorSecondary', + ], + }, + { + header: 'Accent Fill', + colors: [ + 'SystemAccentColor', + 'AccentFillColorDisabled', + ], + }, + { + header: 'Text', + colors: [ + 'TextFillColorPrimary', + 'TextFillColorSecondary', + 'TextFillColorDisabled', + ], + }, + { + header: 'Text On Accent', + colors: [ + 'TextOnAccentFillColorPrimary', + 'TextOnAccentFillColorDisabled', + ], + }, + { + header: 'Control Stroke', + colors: [ + 'ControlStrokeColorDefault', + 'ControlStrokeColorSecondary', + 'ControlStrokeColorOnAccentSecondary', + 'ControlStrongStrokeColorDefault', + 'ControlStrongStrokeColorDisabled', + ], + }, + { + header: 'Other', + colors: [ + 'SolidBackgroundFillColorBase', + 'TextControlForeground', + 'ScrollBarButtonBackground', + 'ScrollBarButtonBackgroundPointerOver', + 'ScrollBarButtonBackgroundPressed', + 'ScrollBarButtonBackgroundDisabled', + 'ScrollBarButtonArrowForeground', + 'ScrollBarButtonArrowForegroundPointerOver', + 'ScrollBarButtonArrowForegroundPressed', + 'ScrollBarButtonArrowForegroundDisabled', + 'ScrollBarThumbFill', + 'ScrollBarThumbFillPointerOver', + 'ScrollBarThumbFillPressed', + 'ScrollBarThumbFillDisabled', + 'ScrollBarTrackFill', + 'ToolTipBackground', + 'ToolTipForeground', + 'ToolTipBorderBrush', + 'SystemChromeMediumLowColor' + ], + }, +]; + + +function PlatformColorList() { + const scrollChildren = []; + const stickyHeaderIndices = []; + let childIndex = 0; + for (const group of platformColorGroups) { + stickyHeaderIndices.push(childIndex); + scrollChildren.push( + {group.header} + ); + childIndex++; + for (const colorKey of group.colors) { + scrollChildren.push( + + ); + childIndex++; + } + } + + return ( + + {scrollChildren} + + ); +} + +export const PlatformColorExamplePage: React.FunctionComponent<{}> = () => { + const example1jsx = ` + + System Accent Color + +`; + + return ( + + + + + System Accent Color + + + + + + + + + ); +}; + + +const styles = StyleSheet.create({ + scrollContent: { + gap: 8, + alignItems: 'flex-start', + }, + sectionHeader: { + fontWeight: '600', + fontSize: 16, + marginTop: 16, + marginBottom: 4, + }, + colorText: { + paddingTop: 6, + paddingBottom: 4, + paddingHorizontal: 14, + borderRadius: 6, + color: '#fff', + fontSize: 15, + overflow: 'hidden', + }, +}); From 39cd0a6874e6e3515d715a63cd4be6f7f7ce7291 Mon Sep 17 00:00:00 2001 From: Chris Glein <26607885+chrisglein@users.noreply.github.com> Date: Wed, 1 Oct 2025 16:43:56 -0700 Subject: [PATCH 2/2] Update PlatformColor pages with pivots, grouping, dark/light backgrounds --- .../src/examples/PlatformColorExamplePage.tsx | 387 ++++++++++++------ src/examples/PlatformColorExamplePage.tsx | 202 +++++---- 2 files changed, 382 insertions(+), 207 deletions(-) diff --git a/NewArch/src/examples/PlatformColorExamplePage.tsx b/NewArch/src/examples/PlatformColorExamplePage.tsx index 4e44e310..2faf16b0 100644 --- a/NewArch/src/examples/PlatformColorExamplePage.tsx +++ b/NewArch/src/examples/PlatformColorExamplePage.tsx @@ -1,143 +1,241 @@ 'use strict'; -import {View, Text, PlatformColor, ScrollView, Pressable, StyleSheet} from 'react-native'; -import React, { useState } from 'react'; +import {View, Text, PlatformColor, ScrollView, StyleSheet, Switch, Button} from 'react-native'; +import React, { useState, createContext, useContext } from 'react'; import {Example} from '../components/Example'; import {Page} from '../components/Page'; -import {useTheme} from '@react-navigation/native'; + +type ThemeContextType = { + isDarkMode: boolean; +}; + +const ThemeContext = createContext({ isDarkMode: false }); -function PlatformColorVisualizer({ colorKey }: { colorKey: string }) { - const [darkText, setDarkText] = useState(false); +type ColorGroup = 'background' | 'border' | 'text'; +type PlatformColorType = { key: string; group?: ColorGroup }; +function PlatformColorVisualizer({ color }: { color: PlatformColorType }) { + const { isDarkMode } = useContext(ThemeContext); + let backgroundStyle; + let textStyle; + if (color.group === 'border') { + backgroundStyle = { borderWidth: 2, borderColor: PlatformColor(color.key), backgroundColor: 'transparent' }; + } else if (color.group === 'text') { + backgroundStyle = { backgroundColor: PlatformColor(color.key) }; + textStyle = { color: 'transparent' }; + } else { + backgroundStyle = { backgroundColor: PlatformColor(color.key) }; + } return ( - setDarkText((prev) => !prev)}> + + + O + - {colorKey} + {color.key} - + ); } // Source for these is here: https://github.com/microsoft/react-native-windows/blob/63e9eaaa2249c3f7b46200203cb69c006c5770a9/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.cpp // Grouping done in alignment with WinUI Gallery's Design->Color section -const platformColorGroups: { header: string; colors: string[] }[] = [ - { - header: 'Control Fill', - colors: [ - 'ControlFillColorDefault', - 'ControlFillColorSecondary', - 'ControlFillColorTertiary', - 'ControlFillColorDisabled', - 'ControlFillColorTransparent', - ], - }, - { - header: 'Control Alt Fill', - colors: [ - 'ControlAltFillColorSecondary', - 'ControlAltFillColorTertiary', - 'ControlAltFillColorQuarternary', - 'ControlAltFillColorDisabled', - ], - }, - { - header: 'Strong Fill', - colors: [ - 'ControlStrongFillColorDefault', - 'ControlStrongFillColorDisabled', - ], - }, - { - header: 'Subtle Fill', - colors: [ - 'SubtleFillColorTransparent', - 'SubtleFillColorSecondary', - ], - }, - { - header: 'Accent Fill', - colors: [ - 'AccentFillColorDefault', - 'AccentFillColorSecondary', - 'AccentFillColorTertiary', - 'AccentFillColorDisabled', - ], - }, - { - header: 'Text', - colors: [ - 'TextFillColorPrimary', - 'TextFillColorSecondary', - 'TextFillColorDisabled', - ], - }, - { - header: 'Text On Accent', - colors: [ - 'TextOnAccentFillColorPrimary', - 'TextOnAccentFillColorDisabled', - ], - }, - { - header: 'Control Stroke', - colors: [ - 'ControlStrokeColorDefault', - 'ControlStrokeColorSecondary', - 'ControlStrokeColorOnAccentSecondary', - 'ControlStrongStrokeColorDefault', - 'ControlStrongStrokeColorDisabled', - ], - }, - { - header: 'Other', - colors: [ - 'SolidBackgroundFillColorBase', - 'CircleElevationBorder', - 'ProgressRingForegroundTheme', - 'TextControlForeground', - 'ScrollBarButtonBackground', - 'ScrollBarButtonBackgroundPointerOver', - 'ScrollBarButtonBackgroundPressed', - 'ScrollBarButtonBackgroundDisabled', - 'ScrollBarButtonArrowForeground', - 'ScrollBarButtonArrowForegroundPointerOver', - 'ScrollBarButtonArrowForegroundPressed', - 'ScrollBarButtonArrowForegroundDisabled', - 'ScrollBarThumbFill', - 'ScrollBarThumbFillPointerOver', - 'ScrollBarThumbFillPressed', - 'ScrollBarThumbFillDisabled', - 'ScrollBarTrackFill', - 'ToolTipBackground', - 'ToolTipForeground', - 'ToolTipBorderBrush', - 'SystemChromeMediumLowColor', - 'SystemControlForegroundBaseHighColor', - 'SystemControlTransientBorderColor', - 'FocusVisualPrimary', - 'FocusVisualSecondary', - ], - }, +const platformColorGroups: { header: string; group?: string; colors: PlatformColorType[] }[] = [ + { + header: 'Control Fill', + colors: [ + { key: 'ControlFillColorDefault' }, + { key: 'ControlFillColorSecondary' }, + { key: 'ControlFillColorTertiary' }, + { key: 'ControlFillColorDisabled' }, + { key: 'ControlFillColorTransparent' }, + ], + }, + { + header: 'Strong Fill', + colors: [ + { key: 'ControlStrongFillColorDefault' }, + { key: 'ControlStrongFillColorDisabled' }, + ], + }, + { + header: 'Control Alt Fill', + colors: [ + { key: 'ControlAltFillColorSecondary' }, + { key: 'ControlAltFillColorTertiary' }, + { key: 'ControlAltFillColorQuarternary' }, + { key: 'ControlAltFillColorDisabled' }, + ], + }, + { + header: 'Subtle Fill', + colors: [ + { key: 'SubtleFillColorTransparent' }, + { key: 'SubtleFillColorSecondary' }, + ], + }, + { + header: 'Accent Fill', + colors: [ + { key: 'AccentFillColorDefault' }, + { key: 'AccentFillColorSecondary' }, + { key: 'AccentFillColorTertiary' }, + { key: 'AccentFillColorDisabled' }, + ], + }, + { + header: 'Accent', + group: 'system', + colors: [ + { key: 'Accent' }, + { key: 'AccentDark1' }, + { key: 'AccentDark2' }, + { key: 'AccentDark3' }, + { key: 'AccentLight1' }, + { key: 'AccentLight2' }, + { key: 'AccentLight3' }, + //{ key: 'Complement' }, + { key: 'Foreground' }, + ], + }, + { + header: 'Text', + colors: [ + { key: 'TextFillColorPrimary', group: 'text' }, + { key: 'TextFillColorSecondary', group: 'text' }, + { key: 'TextFillColorDisabled', group: 'text' }, + ], + }, + { + header: 'Text On Accent', + colors: [ + { key: 'TextOnAccentFillColorPrimary', group: 'text' }, + { key: 'TextOnAccentFillColorDisabled', group: 'text' }, + ], + }, + { + header: 'Control Stroke', + colors: [ + { key: 'ControlStrokeColorDefault', group: 'border' }, + { key: 'ControlStrokeColorSecondary', group: 'border' }, + { key: 'ControlStrokeColorOnAccentSecondary', group: 'border' }, + { key: 'ControlStrongStrokeColorDefault', group: 'border' }, + { key: 'ControlStrongStrokeColorDisabled', group: 'border' }, + ], + }, + { + header: 'Other', + colors: [ + { key: 'SolidBackgroundFillColorBase' }, + { key: 'CircleElevationBorder', group: 'border' }, + { key: 'ProgressRingForegroundTheme' }, + { key: 'TextControlForeground' }, + { key: 'ScrollBarButtonBackground' }, + { key: 'ScrollBarButtonBackgroundPointerOver' }, + { key: 'ScrollBarButtonBackgroundPressed' }, + { key: 'ScrollBarButtonBackgroundDisabled' }, + { key: 'ScrollBarButtonArrowForeground' }, + { key: 'ScrollBarButtonArrowForegroundPointerOver' }, + { key: 'ScrollBarButtonArrowForegroundPressed' }, + { key: 'ScrollBarButtonArrowForegroundDisabled' }, + { key: 'ScrollBarThumbFill' }, + { key: 'ScrollBarThumbFillPointerOver' }, + { key: 'ScrollBarThumbFillPressed' }, + { key: 'ScrollBarThumbFillDisabled' }, + { key: 'ScrollBarTrackFill' }, + { key: 'ToolTipBackground' }, + { key: 'ToolTipForeground' }, + { key: 'ToolTipBorderBrush', group: 'border' }, + { key: 'SystemChromeMediumLowColor' }, + { key: 'SystemControlForegroundBaseHighColor' }, + { key: 'SystemControlTransientBorderColor', group: 'border' }, + { key: 'FocusVisualPrimary', group: 'border' }, + { key: 'FocusVisualSecondary', group: 'border' }, + ], + }, + { + header: 'System', + group: 'system', + colors: [ + { key: 'AccentColor' }, + { key: 'ActiveCaption' }, + { key: 'Background' }, + { key: 'ButtonFace' }, + { key: 'ButtonText' }, + { key: 'CaptionText' }, + { key: 'GrayText' }, + { key: 'Highlight' }, + { key: 'HighlightText' }, + { key: 'Hotlight' }, + { key: 'InactiveCaption' }, + { key: 'InactiveCaptionText' }, + { key: 'NonTextHigh' }, + { key: 'NonTextLow' }, + { key: 'NonTextMedium' }, + { key: 'NonTextMediumHigh' }, + { key: 'NonTextMediumLow' }, + { key: 'OverlayOutsidePopup' }, + { key: 'PageBackground' }, + { key: 'PopupBackground' }, + { key: 'TextContrastWithHigh' }, + { key: 'TextHigh' }, + { key: 'TextLow' }, + { key: 'TextMedium' }, + { key: 'Window' }, + { key: 'WindowText' }, + ], + }, ]; -function PlatformColorList() { +const groupLabels: { group: ColorGroup | 'fill' | 'system'; label: string }[] = [ + { group: 'background', label: 'Fill' }, + { group: 'border', label: 'Stroke' }, + { group: 'text', label: 'Text' }, + { group: 'system', label: 'System' }, +]; + +function PlatformColorList({ groupFilter }: { groupFilter: ColorGroup | 'fill' | 'system' }) { + const { isDarkMode } = useContext(ThemeContext); const scrollChildren = []; const stickyHeaderIndices = []; let childIndex = 0; for (const group of platformColorGroups) { + // Filter colors by group + const filteredColors = group.colors.filter(color => { + if (groupFilter === 'system') { + return group.header === 'System' || group.header === 'Accent'; + } + if (groupFilter === 'background') { + // Exclude System and Accent groups from background filter + if (group.header === 'System' || group.header === 'Accent') { + return false; + } + return !color.group || color.group === 'background'; + } + return color.group === groupFilter; + }); + if (filteredColors.length === 0) continue; stickyHeaderIndices.push(childIndex); scrollChildren.push( - {group.header} + {group.header} ); childIndex++; - for (const colorKey of group.colors) { + for (const color of filteredColors) { scrollChildren.push( - + ); childIndex++; } @@ -145,25 +243,52 @@ function PlatformColorList() { return ( - {scrollChildren} + {scrollChildren} ); } +function PlatformColorValueList() { + const [isDarkMode, setIsDarkMode] = useState(false); + const [groupFilter, setGroupFilter] = useState('background'); + + return ( + + + Dark Background + + + + {groupLabels.map(({ group, label }) => ( + +