@yahoo/uds-mobile 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +1174 -0
- package/README.md +614 -0
- package/cli/uds-mobile.js +10 -0
- package/dist/_virtual/rolldown_runtime.cjs +30 -0
- package/dist/bin/generateTheme.mjs +390 -0
- package/dist/bin/mobile/scripts/utils/configToRNMappings.mjs +74 -0
- package/dist/bin/uds/dist/fixtures.mjs +404 -0
- package/dist/bin/uds/dist/tokens/configs/palette/alwaysPalette.mjs +23 -0
- package/dist/bin/uds/dist/tokens/consts/defaultModes.mjs +6 -0
- package/dist/bin/uds/dist/tokens/consts/fontDeclarationsMap.mjs +652 -0
- package/dist/bin/uds-mobile.mjs +256 -0
- package/dist/components/Avatar.cjs +109 -0
- package/dist/components/Avatar.d.cts +60 -0
- package/dist/components/Avatar.d.cts.map +1 -0
- package/dist/components/Avatar.d.mts +60 -0
- package/dist/components/Avatar.d.mts.map +1 -0
- package/dist/components/Avatar.mjs +109 -0
- package/dist/components/Avatar.mjs.map +1 -0
- package/dist/components/Badge.cjs +68 -0
- package/dist/components/Badge.d.cts +43 -0
- package/dist/components/Badge.d.cts.map +1 -0
- package/dist/components/Badge.d.mts +43 -0
- package/dist/components/Badge.d.mts.map +1 -0
- package/dist/components/Badge.mjs +68 -0
- package/dist/components/Badge.mjs.map +1 -0
- package/dist/components/Box.cjs +114 -0
- package/dist/components/Box.d.cts +87 -0
- package/dist/components/Box.d.cts.map +1 -0
- package/dist/components/Box.d.mts +87 -0
- package/dist/components/Box.d.mts.map +1 -0
- package/dist/components/Box.mjs +114 -0
- package/dist/components/Box.mjs.map +1 -0
- package/dist/components/Button.cjs +187 -0
- package/dist/components/Button.d.cts +56 -0
- package/dist/components/Button.d.cts.map +1 -0
- package/dist/components/Button.d.mts +56 -0
- package/dist/components/Button.d.mts.map +1 -0
- package/dist/components/Button.mjs +186 -0
- package/dist/components/Button.mjs.map +1 -0
- package/dist/components/Checkbox.cjs +156 -0
- package/dist/components/Checkbox.d.cts +48 -0
- package/dist/components/Checkbox.d.cts.map +1 -0
- package/dist/components/Checkbox.d.mts +48 -0
- package/dist/components/Checkbox.d.mts.map +1 -0
- package/dist/components/Checkbox.mjs +155 -0
- package/dist/components/Checkbox.mjs.map +1 -0
- package/dist/components/Chip.cjs +124 -0
- package/dist/components/Chip.d.cts +60 -0
- package/dist/components/Chip.d.cts.map +1 -0
- package/dist/components/Chip.d.mts +60 -0
- package/dist/components/Chip.d.mts.map +1 -0
- package/dist/components/Chip.mjs +124 -0
- package/dist/components/Chip.mjs.map +1 -0
- package/dist/components/HStack.cjs +46 -0
- package/dist/components/HStack.d.cts +41 -0
- package/dist/components/HStack.d.cts.map +1 -0
- package/dist/components/HStack.d.mts +41 -0
- package/dist/components/HStack.d.mts.map +1 -0
- package/dist/components/HStack.mjs +46 -0
- package/dist/components/HStack.mjs.map +1 -0
- package/dist/components/Icon.cjs +150 -0
- package/dist/components/Icon.d.cts +90 -0
- package/dist/components/Icon.d.cts.map +1 -0
- package/dist/components/Icon.d.mts +90 -0
- package/dist/components/Icon.d.mts.map +1 -0
- package/dist/components/Icon.mjs +148 -0
- package/dist/components/Icon.mjs.map +1 -0
- package/dist/components/IconButton.cjs +127 -0
- package/dist/components/IconButton.d.cts +46 -0
- package/dist/components/IconButton.d.cts.map +1 -0
- package/dist/components/IconButton.d.mts +46 -0
- package/dist/components/IconButton.d.mts.map +1 -0
- package/dist/components/IconButton.mjs +127 -0
- package/dist/components/IconButton.mjs.map +1 -0
- package/dist/components/IconSlot.cjs +55 -0
- package/dist/components/IconSlot.d.cts +45 -0
- package/dist/components/IconSlot.d.cts.map +1 -0
- package/dist/components/IconSlot.d.mts +45 -0
- package/dist/components/IconSlot.d.mts.map +1 -0
- package/dist/components/IconSlot.mjs +55 -0
- package/dist/components/IconSlot.mjs.map +1 -0
- package/dist/components/Image.cjs +104 -0
- package/dist/components/Image.d.cts +85 -0
- package/dist/components/Image.d.cts.map +1 -0
- package/dist/components/Image.d.mts +85 -0
- package/dist/components/Image.d.mts.map +1 -0
- package/dist/components/Image.mjs +104 -0
- package/dist/components/Image.mjs.map +1 -0
- package/dist/components/Input.cjs +174 -0
- package/dist/components/Input.d.cts +39 -0
- package/dist/components/Input.d.cts.map +1 -0
- package/dist/components/Input.d.mts +39 -0
- package/dist/components/Input.d.mts.map +1 -0
- package/dist/components/Input.mjs +174 -0
- package/dist/components/Input.mjs.map +1 -0
- package/dist/components/Link.cjs +139 -0
- package/dist/components/Link.d.cts +45 -0
- package/dist/components/Link.d.cts.map +1 -0
- package/dist/components/Link.d.mts +45 -0
- package/dist/components/Link.d.mts.map +1 -0
- package/dist/components/Link.mjs +138 -0
- package/dist/components/Link.mjs.map +1 -0
- package/dist/components/Pressable.cjs +120 -0
- package/dist/components/Pressable.d.cts +90 -0
- package/dist/components/Pressable.d.cts.map +1 -0
- package/dist/components/Pressable.d.mts +90 -0
- package/dist/components/Pressable.d.mts.map +1 -0
- package/dist/components/Pressable.mjs +118 -0
- package/dist/components/Pressable.mjs.map +1 -0
- package/dist/components/Radio.cjs +153 -0
- package/dist/components/Radio.d.cts +46 -0
- package/dist/components/Radio.d.cts.map +1 -0
- package/dist/components/Radio.d.mts +46 -0
- package/dist/components/Radio.d.mts.map +1 -0
- package/dist/components/Radio.mjs +152 -0
- package/dist/components/Radio.mjs.map +1 -0
- package/dist/components/Screen.cjs +89 -0
- package/dist/components/Screen.d.cts +53 -0
- package/dist/components/Screen.d.cts.map +1 -0
- package/dist/components/Screen.d.mts +53 -0
- package/dist/components/Screen.d.mts.map +1 -0
- package/dist/components/Screen.mjs +89 -0
- package/dist/components/Screen.mjs.map +1 -0
- package/dist/components/Switch.cjs +172 -0
- package/dist/components/Switch.d.cts +42 -0
- package/dist/components/Switch.d.cts.map +1 -0
- package/dist/components/Switch.d.mts +42 -0
- package/dist/components/Switch.d.mts.map +1 -0
- package/dist/components/Switch.mjs +171 -0
- package/dist/components/Switch.mjs.map +1 -0
- package/dist/components/Text.cjs +107 -0
- package/dist/components/Text.d.cts +96 -0
- package/dist/components/Text.d.cts.map +1 -0
- package/dist/components/Text.d.mts +96 -0
- package/dist/components/Text.d.mts.map +1 -0
- package/dist/components/Text.mjs +107 -0
- package/dist/components/Text.mjs.map +1 -0
- package/dist/components/VStack.cjs +46 -0
- package/dist/components/VStack.d.cts +41 -0
- package/dist/components/VStack.d.cts.map +1 -0
- package/dist/components/VStack.d.mts +41 -0
- package/dist/components/VStack.d.mts.map +1 -0
- package/dist/components/VStack.mjs +46 -0
- package/dist/components/VStack.mjs.map +1 -0
- package/dist/icons/dist/glyphMap.cjs +5475 -0
- package/dist/icons/dist/glyphMap.d.cts +10 -0
- package/dist/icons/dist/glyphMap.d.cts.map +1 -0
- package/dist/icons/dist/glyphMap.d.mts +10 -0
- package/dist/icons/dist/glyphMap.d.mts.map +1 -0
- package/dist/icons/dist/glyphMap.mjs +5474 -0
- package/dist/icons/dist/glyphMap.mjs.map +1 -0
- package/dist/icons/dist/svgMap.cjs +525 -0
- package/dist/icons/dist/svgMap.d.cts +10 -0
- package/dist/icons/dist/svgMap.d.cts.map +1 -0
- package/dist/icons/dist/svgMap.d.mts +10 -0
- package/dist/icons/dist/svgMap.d.mts.map +1 -0
- package/dist/icons/dist/svgMap.mjs +524 -0
- package/dist/icons/dist/svgMap.mjs.map +1 -0
- package/dist/icons/dist/tokens.cjs +13 -0
- package/dist/icons/dist/tokens.mjs +13 -0
- package/dist/icons/dist/tokens.mjs.map +1 -0
- package/dist/icons/dist/types.d.cts +24 -0
- package/dist/icons/dist/types.d.cts.map +1 -0
- package/dist/icons/dist/types.d.mts +24 -0
- package/dist/icons/dist/types.d.mts.map +1 -0
- package/dist/motion.cjs +56 -0
- package/dist/motion.d.cts +46 -0
- package/dist/motion.d.cts.map +1 -0
- package/dist/motion.d.mts +46 -0
- package/dist/motion.d.mts.map +1 -0
- package/dist/motion.mjs +52 -0
- package/dist/motion.mjs.map +1 -0
- package/dist/types.cjs +1 -0
- package/dist/types.d.cts +13 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +13 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +2 -0
- package/dist/uds/dist/components/Box.d.cts +3 -0
- package/dist/uds/dist/components/Box.d.mts +3 -0
- package/dist/uds/dist/components/Divider/Divider.d.cts +3 -0
- package/dist/uds/dist/components/Divider/Divider.d.mts +3 -0
- package/dist/uds/dist/components/Divider/DividerCore.d.cts +4 -0
- package/dist/uds/dist/components/Divider/DividerCore.d.mts +4 -0
- package/dist/uds/dist/components/Divider/DividerInternal.d.cts +4 -0
- package/dist/uds/dist/components/Divider/DividerInternal.d.mts +4 -0
- package/dist/uds/dist/components/Divider/index.d.cts +2 -0
- package/dist/uds/dist/components/Divider/index.d.mts +2 -0
- package/dist/uds/dist/components/FormLabel.d.cts +3 -0
- package/dist/uds/dist/components/FormLabel.d.mts +4 -0
- package/dist/uds/dist/components/HStack.d.cts +4 -0
- package/dist/uds/dist/components/HStack.d.mts +4 -0
- package/dist/uds/dist/components/Icon.d.cts +3 -0
- package/dist/uds/dist/components/Icon.d.mts +3 -0
- package/dist/uds/dist/components/Image.d.cts +2 -0
- package/dist/uds/dist/components/Image.d.mts +3 -0
- package/dist/uds/dist/components/Link.d.cts +3 -0
- package/dist/uds/dist/components/Link.d.mts +3 -0
- package/dist/uds/dist/components/Text.d.cts +3 -0
- package/dist/uds/dist/components/Text.d.mts +3 -0
- package/dist/uds/dist/components/VStack.d.cts +4 -0
- package/dist/uds/dist/components/VStack.d.mts +4 -0
- package/dist/uds/dist/components/client/Avatar/Avatar.d.cts +2 -0
- package/dist/uds/dist/components/client/Avatar/Avatar.d.mts +3 -0
- package/dist/uds/dist/components/client/Avatar/AvatarIcon.d.cts +2 -0
- package/dist/uds/dist/components/client/Avatar/AvatarIcon.d.mts +3 -0
- package/dist/uds/dist/components/client/Avatar/AvatarImage.d.cts +4 -0
- package/dist/uds/dist/components/client/Avatar/AvatarImage.d.mts +5 -0
- package/dist/uds/dist/components/client/Avatar/AvatarText.d.cts +3 -0
- package/dist/uds/dist/components/client/Avatar/AvatarText.d.mts +4 -0
- package/dist/uds/dist/components/client/Avatar/index.d.cts +5 -0
- package/dist/uds/dist/components/client/Avatar/index.d.mts +5 -0
- package/dist/uds/dist/components/client/Badge.d.cts +3 -0
- package/dist/uds/dist/components/client/Badge.d.mts +3 -0
- package/dist/uds/dist/components/client/Button.d.cts +3 -0
- package/dist/uds/dist/components/client/Button.d.mts +3 -0
- package/dist/uds/dist/components/client/Checkbox.d.cts +3 -0
- package/dist/uds/dist/components/client/Checkbox.d.mts +3 -0
- package/dist/uds/dist/components/client/Chip/Chip.d.cts +4 -0
- package/dist/uds/dist/components/client/Chip/Chip.d.mts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipBase.d.cts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipBase.d.mts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipButton.d.cts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipButton.d.mts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipDismissible.d.cts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipDismissible.d.mts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipLink.d.cts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipLink.d.mts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipToggle.d.cts +4 -0
- package/dist/uds/dist/components/client/Chip/ChipToggle.d.mts +4 -0
- package/dist/uds/dist/components/client/Chip/index.d.cts +6 -0
- package/dist/uds/dist/components/client/Chip/index.d.mts +6 -0
- package/dist/uds/dist/components/client/IconButton.d.cts +3 -0
- package/dist/uds/dist/components/client/IconButton.d.mts +3 -0
- package/dist/uds/dist/components/client/Input/Input.d.cts +4 -0
- package/dist/uds/dist/components/client/Input/Input.d.mts +4 -0
- package/dist/uds/dist/components/client/Input/InputHelpText.d.cts +2 -0
- package/dist/uds/dist/components/client/Input/InputHelpText.d.mts +3 -0
- package/dist/uds/dist/components/client/Input/InputHelpTextInternal.d.cts +5 -0
- package/dist/uds/dist/components/client/Input/InputHelpTextInternal.d.mts +6 -0
- package/dist/uds/dist/components/client/Input/index.d.cts +3 -0
- package/dist/uds/dist/components/client/Input/index.d.mts +3 -0
- package/dist/uds/dist/components/client/Menu/Menu.Content.d.cts +4 -0
- package/dist/uds/dist/components/client/Menu/Menu.Content.d.mts +4 -0
- package/dist/uds/dist/components/client/Menu/Menu.Divider.d.cts +3 -0
- package/dist/uds/dist/components/client/Menu/Menu.Divider.d.mts +3 -0
- package/dist/uds/dist/components/client/Menu/Menu.Item.d.cts +4 -0
- package/dist/uds/dist/components/client/Menu/Menu.Item.d.mts +4 -0
- package/dist/uds/dist/components/client/Menu/Menu.ItemCheckbox.d.cts +4 -0
- package/dist/uds/dist/components/client/Menu/Menu.ItemCheckbox.d.mts +4 -0
- package/dist/uds/dist/components/client/Menu/Menu.Provider.d.cts +2 -0
- package/dist/uds/dist/components/client/Menu/Menu.Provider.d.mts +3 -0
- package/dist/uds/dist/components/client/Menu/Menu.Trigger.d.cts +4 -0
- package/dist/uds/dist/components/client/Menu/Menu.Trigger.d.mts +4 -0
- package/dist/uds/dist/components/client/Menu/Menu.d.cts +2 -0
- package/dist/uds/dist/components/client/Menu/Menu.d.mts +2 -0
- package/dist/uds/dist/components/client/Menu/Menu.index.d.cts +7 -0
- package/dist/uds/dist/components/client/Menu/Menu.index.d.mts +7 -0
- package/dist/uds/dist/components/client/Menu/index.d.cts +9 -0
- package/dist/uds/dist/components/client/Menu/index.d.mts +9 -0
- package/dist/uds/dist/components/client/Pressable.d.cts +3 -0
- package/dist/uds/dist/components/client/Pressable.d.mts +3 -0
- package/dist/uds/dist/components/client/Radio/Radio.d.cts +3 -0
- package/dist/uds/dist/components/client/Radio/Radio.d.mts +3 -0
- package/dist/uds/dist/components/client/Radio/RadioGroupProvider.d.cts +3 -0
- package/dist/uds/dist/components/client/Radio/RadioGroupProvider.d.mts +3 -0
- package/dist/uds/dist/components/client/Radio/index.d.cts +3 -0
- package/dist/uds/dist/components/client/Radio/index.d.mts +3 -0
- package/dist/uds/dist/components/client/SpringMotionConfig.d.cts +3 -0
- package/dist/uds/dist/components/client/SpringMotionConfig.d.mts +3 -0
- package/dist/uds/dist/components/client/Switch.d.cts +3 -0
- package/dist/uds/dist/components/client/Switch.d.mts +3 -0
- package/dist/uds/dist/components/client/index.d.cts +33 -0
- package/dist/uds/dist/components/client/index.d.mts +33 -0
- package/dist/uds/dist/components/index.d.cts +40 -0
- package/dist/uds/dist/components/index.d.mts +40 -0
- package/dist/uds/dist/index.d.cts +46 -0
- package/dist/uds/dist/index.d.mts +46 -0
- package/dist/uds/dist/styles/styler.d.cts +2 -0
- package/dist/uds/dist/styles/styler.d.mts +2 -0
- package/dist/uds/dist/styles/stylerTypes.d.cts +2 -0
- package/dist/uds/dist/styles/stylerTypes.d.mts +2 -0
- package/dist/uds/dist/tokens/automation/configs/avatar.d.cts +2 -0
- package/dist/uds/dist/tokens/automation/configs/avatar.d.mts +2 -0
- package/dist/uds/dist/tokens/automation/properties.d.cts +3 -0
- package/dist/uds/dist/tokens/automation/properties.d.mts +3 -0
- package/dist/uds/dist/tokens/automation/types/ComponentConfig.d.cts +3 -0
- package/dist/uds/dist/tokens/automation/types/ComponentConfig.d.mts +3 -0
- package/dist/uds/dist/tokens/configs/motion.cjs +109 -0
- package/dist/uds/dist/tokens/configs/motion.d.cts +20 -0
- package/dist/uds/dist/tokens/configs/motion.d.cts.map +1 -0
- package/dist/uds/dist/tokens/configs/motion.d.mts +20 -0
- package/dist/uds/dist/tokens/configs/motion.d.mts.map +1 -0
- package/dist/uds/dist/tokens/configs/motion.mjs +108 -0
- package/dist/uds/dist/tokens/configs/motion.mjs.map +1 -0
- package/dist/uds/dist/tokens/configs/palette/alwaysPalette.d.cts +32 -0
- package/dist/uds/dist/tokens/configs/palette/alwaysPalette.d.cts.map +1 -0
- package/dist/uds/dist/tokens/configs/palette/alwaysPalette.d.mts +32 -0
- package/dist/uds/dist/tokens/configs/palette/alwaysPalette.d.mts.map +1 -0
- package/dist/uds/dist/tokens/types.d.cts +426 -0
- package/dist/uds/dist/tokens/types.d.cts.map +1 -0
- package/dist/uds/dist/tokens/types.d.mts +426 -0
- package/dist/uds/dist/tokens/types.d.mts.map +1 -0
- package/dist/uds/dist/types.d.cts +2 -0
- package/dist/uds/dist/types.d.mts +2 -0
- package/fonts/centra-no2-black-italic.otf +0 -0
- package/fonts/centra-no2-black.otf +0 -0
- package/fonts/centra-no2-bold-italic.otf +0 -0
- package/fonts/centra-no2-bold.otf +0 -0
- package/fonts/centra-no2-book-italic.otf +0 -0
- package/fonts/centra-no2-book.otf +0 -0
- package/fonts/centra-no2-extrabold-italic.otf +0 -0
- package/fonts/centra-no2-extrabold.otf +0 -0
- package/fonts/centra-no2-hairline-italic.otf +0 -0
- package/fonts/centra-no2-hairline.otf +0 -0
- package/fonts/centra-no2-light-italic.otf +0 -0
- package/fonts/centra-no2-light.otf +0 -0
- package/fonts/centra-no2-medium-italic.otf +0 -0
- package/fonts/centra-no2-medium.otf +0 -0
- package/fonts/centra-no2-thin-italic.otf +0 -0
- package/fonts/centra-no2-thin.otf +0 -0
- package/fonts/gelica-black-italic.otf +0 -0
- package/fonts/gelica-black.otf +0 -0
- package/fonts/gelica-bold-italic.otf +0 -0
- package/fonts/gelica-bold.otf +0 -0
- package/fonts/gelica-extra-light-italic.otf +0 -0
- package/fonts/gelica-extra-light.otf +0 -0
- package/fonts/gelica-light-italic.otf +0 -0
- package/fonts/gelica-light.otf +0 -0
- package/fonts/gelica-medium-italic.otf +0 -0
- package/fonts/gelica-medium.otf +0 -0
- package/fonts/gelica-regular-italic.otf +0 -0
- package/fonts/gelica-regular.otf +0 -0
- package/fonts/gelica-semi-bold-italic.otf +0 -0
- package/fonts/gelica-semi-bold.otf +0 -0
- package/fonts/index.cjs +270 -0
- package/fonts/index.d.ts +10 -0
- package/fonts/index.mjs +272 -0
- package/fonts/inter-black.ttf +0 -0
- package/fonts/inter-bold.ttf +0 -0
- package/fonts/inter-extra-bold.ttf +0 -0
- package/fonts/inter-extra-light.ttf +0 -0
- package/fonts/inter-light.ttf +0 -0
- package/fonts/inter-medium.ttf +0 -0
- package/fonts/inter-regular.ttf +0 -0
- package/fonts/inter-semi-bold.ttf +0 -0
- package/fonts/inter-thin.ttf +0 -0
- package/fonts/roboto-mono-bold-italic.ttf +0 -0
- package/fonts/roboto-mono-bold.ttf +0 -0
- package/fonts/roboto-mono-light-italic.ttf +0 -0
- package/fonts/roboto-mono-light.ttf +0 -0
- package/fonts/roboto-mono-medium-italic.ttf +0 -0
- package/fonts/roboto-mono-medium.ttf +0 -0
- package/fonts/roboto-mono-regular-italic.ttf +0 -0
- package/fonts/roboto-mono-regular.ttf +0 -0
- package/fonts/roboto-mono-thin-italic.ttf +0 -0
- package/fonts/roboto-mono-thin.ttf +0 -0
- package/fonts/uds-icons.ttf +0 -0
- package/fonts/yahoo-product-sans-bold.otf +0 -0
- package/fonts/yahoo-product-sans-extended-black-italic.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-black.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-bold-italic.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-bold.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-light-italic.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-light.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-medium-italic.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-medium.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-regular-italic.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-regular.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-semibold-italic.ttf +0 -0
- package/fonts/yahoo-product-sans-extended-semibold.ttf +0 -0
- package/fonts/yahoo-product-sans-medium.otf +0 -0
- package/fonts/yahoo-product-sans-regular.otf +0 -0
- package/fonts/yahoo-sans-beta-bold.otf +0 -0
- package/fonts/yahoo-sans-beta-medium.otf +0 -0
- package/fonts/yahoo-sans-beta-regular.otf +0 -0
- package/fonts/yahoo-sans-black.otf +0 -0
- package/fonts/yahoo-sans-bold.otf +0 -0
- package/fonts/yahoo-sans-condensed-black.otf +0 -0
- package/fonts/yahoo-sans-condensed-bold.otf +0 -0
- package/fonts/yahoo-sans-condensed-light.otf +0 -0
- package/fonts/yahoo-sans-condensed-medium.otf +0 -0
- package/fonts/yahoo-sans-condensed-regular.otf +0 -0
- package/fonts/yahoo-sans-condensed-xbold.ttf +0 -0
- package/fonts/yahoo-sans-cr4-bold.ttf +0 -0
- package/fonts/yahoo-sans-cr4-medium.ttf +0 -0
- package/fonts/yahoo-sans-cr4-regular.ttf +0 -0
- package/fonts/yahoo-sans-extrabold.otf +0 -0
- package/fonts/yahoo-sans-extralight.otf +0 -0
- package/fonts/yahoo-sans-italic.otf +0 -0
- package/fonts/yahoo-sans-light.otf +0 -0
- package/fonts/yahoo-sans-medium.otf +0 -0
- package/fonts/yahoo-sans-regular-italic.otf +0 -0
- package/fonts/yahoo-sans-regular.otf +0 -0
- package/fonts/yahoo-sans-semibold.otf +0 -0
- package/fonts/yahoo-serif-display-black.otf +0 -0
- package/fonts/yahoo-serif-display-bold.otf +0 -0
- package/fonts/yahoo-serif-display-extrabold.otf +0 -0
- package/fonts/yahoo-serif-display-light.otf +0 -0
- package/fonts/yahoo-serif-display-regular.otf +0 -0
- package/fonts/yahoo-serif-text-bold-italic.ttf +0 -0
- package/fonts/yahoo-serif-text-bold.otf +0 -0
- package/fonts/yahoo-serif-text-italic.otf +0 -0
- package/fonts/yahoo-serif-text-regular.otf +0 -0
- package/fonts/yas-black-italic.otf +0 -0
- package/fonts/yas-black.otf +0 -0
- package/fonts/yas-bold-italic.otf +0 -0
- package/fonts/yas-bold.otf +0 -0
- package/fonts/yas-condensed-black-italic.otf +0 -0
- package/fonts/yas-condensed-black.otf +0 -0
- package/fonts/yas-condensed-bold-italic.otf +0 -0
- package/fonts/yas-condensed-bold.otf +0 -0
- package/fonts/yas-condensed-light-italic.otf +0 -0
- package/fonts/yas-condensed-light.otf +0 -0
- package/fonts/yas-condensed-medium-italic.otf +0 -0
- package/fonts/yas-condensed-medium.otf +0 -0
- package/fonts/yas-condensed-regular-italic.otf +0 -0
- package/fonts/yas-condensed-regular.otf +0 -0
- package/fonts/yas-condensed-semibold-italic.otf +0 -0
- package/fonts/yas-condensed-semibold.otf +0 -0
- package/fonts/yas-extended-black-italic.otf +0 -0
- package/fonts/yas-extended-black.otf +0 -0
- package/fonts/yas-extended-bold-italic.otf +0 -0
- package/fonts/yas-extended-bold.otf +0 -0
- package/fonts/yas-extended-light-italic.otf +0 -0
- package/fonts/yas-extended-light.otf +0 -0
- package/fonts/yas-extended-medium-italic.otf +0 -0
- package/fonts/yas-extended-medium.otf +0 -0
- package/fonts/yas-extended-regular-italic.otf +0 -0
- package/fonts/yas-extended-regular.otf +0 -0
- package/fonts/yas-extended-semibold-italic.otf +0 -0
- package/fonts/yas-extended-semibold.otf +0 -0
- package/fonts/yas-light-italic.otf +0 -0
- package/fonts/yas-light.otf +0 -0
- package/fonts/yas-medium-italic.otf +0 -0
- package/fonts/yas-medium.otf +0 -0
- package/fonts/yas-regular-italic.otf +0 -0
- package/fonts/yas-regular.otf +0 -0
- package/fonts/yas-semibold-italic.otf +0 -0
- package/fonts/yas-semibold.otf +0 -0
- package/generated/styles.cjs +4510 -0
- package/generated/styles.d.cts +1 -0
- package/generated/styles.d.mts +1 -0
- package/generated/styles.d.ts +2965 -0
- package/generated/styles.mjs +4510 -0
- package/generated/unistyles.d.ts +4320 -0
- package/package.json +308 -0
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
# Contributing to @yahoo/uds-mobile
|
|
2
|
+
|
|
3
|
+
This guide covers the internal architecture and development workflow for the UDS Mobile package.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Architecture Overview](#architecture-overview)
|
|
8
|
+
- [Architecture Proposals](#architecture-proposals)
|
|
9
|
+
- [Dependency Bundling](#dependency-bundling)
|
|
10
|
+
- [Generated Files](#generated-files)
|
|
11
|
+
- [Code Generation Pipeline](#code-generation-pipeline)
|
|
12
|
+
- [Theme Generation](#theme-generation)
|
|
13
|
+
- [Component Styling System](#component-styling-system)
|
|
14
|
+
- [Unistyles Patterns](#unistyles-patterns)
|
|
15
|
+
- [Reanimated + Unistyles](#reanimated--unistyles)
|
|
16
|
+
- [Development Workflow](#development-workflow)
|
|
17
|
+
- [Adding New Components](#adding-new-components)
|
|
18
|
+
- [Debugging](#debugging)
|
|
19
|
+
|
|
20
|
+
## Architecture Overview
|
|
21
|
+
|
|
22
|
+
The `@yahoo/uds-mobile` package provides React Native components styled with [Unistyles](https://www.unistyl.es//). The package has two distinct concerns:
|
|
23
|
+
|
|
24
|
+
1. **Library Code** (`src/`): Components, hooks, and utilities
|
|
25
|
+
2. **Generated Code** (`generated/`): Auto-generated styles and types from UDS tokens
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
packages/mobile/
|
|
29
|
+
├── bin/ # CLI tools for consumers
|
|
30
|
+
│ ├── uds-mobile.ts # Main CLI entry (sync command)
|
|
31
|
+
│ └── generateTheme.ts # Theme generation logic
|
|
32
|
+
├── scripts/ # Internal code generation
|
|
33
|
+
│ ├── generate.ts # Main orchestrator
|
|
34
|
+
│ ├── generateStyles.ts # Generates styles.ts
|
|
35
|
+
│ ├── generateTypes.ts # Generates unistyles.d.ts
|
|
36
|
+
│ ├── generateFonts.ts # Generates font manifests
|
|
37
|
+
│ └── utils/ # Generation utilities
|
|
38
|
+
├── src/
|
|
39
|
+
│ ├── components/ # React Native components
|
|
40
|
+
│ ├── generated/ # AUTO-GENERATED (do not edit)
|
|
41
|
+
│ │ ├── styles.ts # Unistyles variants
|
|
42
|
+
│ │ └── unistyles.d.ts # Type declarations
|
|
43
|
+
│ ├── types.ts # Exported types
|
|
44
|
+
│ ├── motion.ts # Animation utilities
|
|
45
|
+
│ └── index.ts # Public exports
|
|
46
|
+
├── fonts/ # Font files + generated manifests
|
|
47
|
+
└── docs/
|
|
48
|
+
├── proposals/ # Architecture proposals and recommendations
|
|
49
|
+
└── *.md # Architecture decision records and issue documentation
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Architecture Proposals
|
|
53
|
+
|
|
54
|
+
See [docs/proposals/](./docs/proposals/) for architectural proposals and recommendations for improving the mobile package and UDS ecosystem:
|
|
55
|
+
|
|
56
|
+
- **[PACKAGE_DECOMPOSITION_BENEFITS.md](./docs/proposals/PACKAGE_DECOMPOSITION_BENEFITS.md)** - Proposal to extract shared types, fixtures, and motion tokens into separate packages
|
|
57
|
+
- **[CONFIG_TRANSFORMATION_ARCHITECTURE.md](./docs/proposals/CONFIG_TRANSFORMATION_ARCHITECTURE.md)** - Proposal for platform-specific config transformation architecture
|
|
58
|
+
- **[HARDCODED_VALUES.md](./docs/proposals/HARDCODED_VALUES.md)** - Documentation of hardcoded values that should come from design tokens
|
|
59
|
+
|
|
60
|
+
## Dependency Bundling
|
|
61
|
+
|
|
62
|
+
The mobile package bundles both `@yahoo/uds` and `@yahoo/uds-icons` into its distribution. This means consumers do **not** need to install these packages separately.
|
|
63
|
+
|
|
64
|
+
### What Gets Bundled
|
|
65
|
+
|
|
66
|
+
- **Runtime JS**: Both packages' JavaScript code via `noExternal: ['@yahoo/uds', '@yahoo/uds-icons']`
|
|
67
|
+
- **TypeScript types**: Both packages' type definitions via `dts.resolve: ['@yahoo/uds', '@yahoo/uds-icons']`
|
|
68
|
+
|
|
69
|
+
This produces two folders in `dist/`:
|
|
70
|
+
|
|
71
|
+
- `dist/uds/` - Bundled `@yahoo/uds` code and types
|
|
72
|
+
- `dist/icons/` - Bundled `@yahoo/uds-icons` code and types
|
|
73
|
+
|
|
74
|
+
### Why Both Packages Are Bundled Together
|
|
75
|
+
|
|
76
|
+
We bundle both packages because of a technical constraint in TypeScript declaration bundling: **you cannot bundle `@yahoo/uds` types without also bundling `@yahoo/uds-icons` types**.
|
|
77
|
+
|
|
78
|
+
The `@yahoo/uds` web package has components that directly import from `@yahoo/uds-icons` (e.g., `ChipDismissible` imports `Cross`, `Menu.ItemCheckbox` imports `Check`). When `dts.resolve` bundles `@yahoo/uds` types, it follows these import references transitively and automatically bundles `@yahoo/uds-icons` types as well.
|
|
79
|
+
|
|
80
|
+
There is no way to configure the bundler to "bundle A but keep B external" when A references B - it's all-or-nothing.
|
|
81
|
+
|
|
82
|
+
**See [docs/BUNDLING_UDS_AND_ICONS.md](./docs/BUNDLING_UDS_AND_ICONS.md) for:**
|
|
83
|
+
|
|
84
|
+
- Detailed technical explanation
|
|
85
|
+
- Why standard solutions (external array, subpath imports) don't work
|
|
86
|
+
- Future roadmap for making `@yahoo/uds-icons` a peer dependency
|
|
87
|
+
|
|
88
|
+
## Generated Files
|
|
89
|
+
|
|
90
|
+
### `generated/styles.ts`
|
|
91
|
+
|
|
92
|
+
This is the most important generated file. It contains all Unistyles stylesheets that map UDS design tokens to React Native styles.
|
|
93
|
+
|
|
94
|
+
**What it contains:**
|
|
95
|
+
|
|
96
|
+
1. **Foundation styles** (`styles.foundation`): Base style variants for layout props
|
|
97
|
+
- `backgroundColor`, `borderRadius`, `spacing`, `color`, `fontFamily`, etc.
|
|
98
|
+
- Maps UDS token names to theme paths (e.g., `'primary'` → `theme.colors.background['primary']`)
|
|
99
|
+
|
|
100
|
+
2. **Component styles** (`avatarStyles`, `buttonStyles`, etc.): Per-component stylesheets
|
|
101
|
+
- Uses Unistyles `variants` and `compoundVariants` for state management
|
|
102
|
+
- Each component has layers: `root`, `text`, `icon`, etc. matching structure from UDS config.
|
|
103
|
+
|
|
104
|
+
**Example input** (from `uds.config.ts` - see `packages/configurator/uds.config.ts`):
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import type { UniversalTokensConfig } from '@yahoo/uds';
|
|
108
|
+
|
|
109
|
+
export const config: UniversalTokensConfig = {
|
|
110
|
+
// Component configs use path-based keys: '{dimension}/{value}/{layer}'
|
|
111
|
+
button: {
|
|
112
|
+
defaults: {
|
|
113
|
+
size: 'md',
|
|
114
|
+
variant: 'primary',
|
|
115
|
+
},
|
|
116
|
+
variables: {
|
|
117
|
+
// Size variants
|
|
118
|
+
'size/lg/root': {
|
|
119
|
+
gap: {
|
|
120
|
+
rest: { type: 'spacingAliases', value: '2', valueType: 'alias' },
|
|
121
|
+
},
|
|
122
|
+
spacingHorizontal: {
|
|
123
|
+
rest: { type: 'spacingAliases', value: '5', valueType: 'alias' },
|
|
124
|
+
},
|
|
125
|
+
textVariant: {
|
|
126
|
+
rest: { type: 'textVariants', value: 'label1', valueType: 'alias' },
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
'size/md/root': {
|
|
130
|
+
gap: {
|
|
131
|
+
rest: { type: 'spacingAliases', value: '1.5', valueType: 'alias' },
|
|
132
|
+
},
|
|
133
|
+
spacingHorizontal: {
|
|
134
|
+
rest: { type: 'spacingAliases', value: '4', valueType: 'alias' },
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
// Variant styles with states (rest, pressed, disabled)
|
|
138
|
+
'variant/primary/root': {
|
|
139
|
+
backgroundColor: {
|
|
140
|
+
rest: { type: 'backgroundPaletteColors', value: 'accent', valueType: 'alias' },
|
|
141
|
+
pressed: { type: 'spectrumColors', value: 'carbon-5', valueType: 'alias' },
|
|
142
|
+
disabled: { type: 'backgroundPaletteColors', value: 'accent', valueType: 'alias' },
|
|
143
|
+
},
|
|
144
|
+
borderRadius: {
|
|
145
|
+
rest: { type: 'borderRadii', value: 'md', valueType: 'alias' },
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
'variant/primary/icon': {
|
|
149
|
+
color: {
|
|
150
|
+
rest: { type: 'foregroundPaletteColors', value: 'on-color', valueType: 'alias' },
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
// ... other components (avatar, badge, checkbox, etc.)
|
|
156
|
+
};
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Example output** (generated `styles.ts`):
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Foundation variants
|
|
163
|
+
export const styles = StyleSheet.create((theme) => ({
|
|
164
|
+
foundation: {
|
|
165
|
+
variants: {
|
|
166
|
+
backgroundColor: {
|
|
167
|
+
primary: { backgroundColor: theme.colors.background['primary'] },
|
|
168
|
+
secondary: { backgroundColor: theme.colors.background['secondary'] },
|
|
169
|
+
// ...
|
|
170
|
+
},
|
|
171
|
+
spacing: {
|
|
172
|
+
'4': { padding: theme.spacing['4'] },
|
|
173
|
+
// ...
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
}));
|
|
178
|
+
|
|
179
|
+
// Component variants
|
|
180
|
+
export const buttonStyles = StyleSheet.create((theme) => ({
|
|
181
|
+
root: {
|
|
182
|
+
variants: {
|
|
183
|
+
size: {
|
|
184
|
+
lg: theme.components['button/size/lg/root/rest'],
|
|
185
|
+
md: theme.components['button/size/md/root/rest'],
|
|
186
|
+
},
|
|
187
|
+
variant: {
|
|
188
|
+
primary: theme.components['button/variant/primary/root/rest'],
|
|
189
|
+
// ...
|
|
190
|
+
},
|
|
191
|
+
disabled: { true: {} },
|
|
192
|
+
pressed: { true: {} },
|
|
193
|
+
},
|
|
194
|
+
compoundVariants: [
|
|
195
|
+
{
|
|
196
|
+
variant: 'primary',
|
|
197
|
+
pressed: true,
|
|
198
|
+
styles: theme.components['button/variant/primary/root/pressed'],
|
|
199
|
+
},
|
|
200
|
+
// ...
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
}));
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Web vs Mobile: Generated Styles Comparison
|
|
207
|
+
|
|
208
|
+
The mobile `styles.ts` serves the same purpose as `packages/uds/src/styles/variants.ts` on web, but with key architectural differences:
|
|
209
|
+
|
|
210
|
+
**Web (`variants.ts`)** - Maps UDS props to **CSS class names**:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Web uses Tailwind-style class names
|
|
214
|
+
export const variants = {
|
|
215
|
+
backgroundColor: {
|
|
216
|
+
primary: 'bg-primary', // CSS class
|
|
217
|
+
secondary: 'bg-secondary',
|
|
218
|
+
accent: 'bg-accent',
|
|
219
|
+
},
|
|
220
|
+
spacing: {
|
|
221
|
+
'4': 'p-4', // Tailwind padding class
|
|
222
|
+
},
|
|
223
|
+
borderRadius: {
|
|
224
|
+
md: 'rounded-md',
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Mobile (`styles.ts`)** - Maps UDS props to **React Native style objects**:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// Mobile uses Unistyles theme references
|
|
233
|
+
export const styles = StyleSheet.create((theme) => ({
|
|
234
|
+
foundation: {
|
|
235
|
+
variants: {
|
|
236
|
+
backgroundColor: {
|
|
237
|
+
primary: { backgroundColor: theme.colors.background['primary'] }, // RN style
|
|
238
|
+
secondary: { backgroundColor: theme.colors.background['secondary'] },
|
|
239
|
+
},
|
|
240
|
+
spacing: {
|
|
241
|
+
'4': { padding: theme.spacing['4'] }, // Numeric value from theme
|
|
242
|
+
},
|
|
243
|
+
borderRadius: {
|
|
244
|
+
md: { borderRadius: theme.borderRadius['md'] },
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
}));
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
| Aspect | Web | Mobile |
|
|
252
|
+
| ------------- | ------------------------------------------------- | ------------------------------------------------------------- |
|
|
253
|
+
| Output format | CSS class names (`'bg-primary'`) | RN style objects (`{ backgroundColor: ... }`) |
|
|
254
|
+
| Theming | CSS custom properties (`var(--color-bg-primary)`) | Unistyles theme object (`theme.colors.background['primary']`) |
|
|
255
|
+
| Dark mode | CSS class toggle (`.dark`) | Unistyles theme switching |
|
|
256
|
+
| File location | `packages/uds/src/styles/variants.ts` | `packages/mobile/generated/styles.ts` |
|
|
257
|
+
|
|
258
|
+
### Component Styles Comparison
|
|
259
|
+
|
|
260
|
+
For component-level styles, the pattern is similar but maps to component-specific theme values:
|
|
261
|
+
|
|
262
|
+
**Web (`autoVariants.ts`)** - Maps component variants to **CSS class names**:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
// packages/uds/generated/autoVariants.ts
|
|
266
|
+
export const autoVariants = {
|
|
267
|
+
buttonVariantRoot: {
|
|
268
|
+
primary: 'uds-button-variant-primary-root', // CSS class
|
|
269
|
+
secondary: 'uds-button-variant-secondary-root',
|
|
270
|
+
brand: 'uds-button-variant-brand-root',
|
|
271
|
+
},
|
|
272
|
+
buttonSizeRoot: {
|
|
273
|
+
lg: 'uds-button-size-lg-root',
|
|
274
|
+
md: 'uds-button-size-md-root',
|
|
275
|
+
sm: 'uds-button-size-sm-root',
|
|
276
|
+
},
|
|
277
|
+
buttonVariantIcon: {
|
|
278
|
+
primary: 'uds-button-variant-primary-icon',
|
|
279
|
+
secondary: 'uds-button-variant-secondary-icon',
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Mobile (`styles.ts`)** - Maps component variants to **theme component style objects**:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// packages/mobile/generated/styles.ts
|
|
288
|
+
export const buttonStyles = StyleSheet.create((theme) => ({
|
|
289
|
+
root: {
|
|
290
|
+
variants: {
|
|
291
|
+
size: {
|
|
292
|
+
lg: theme.components['button/size/lg/root/rest'], // RN style object
|
|
293
|
+
md: theme.components['button/size/md/root/rest'],
|
|
294
|
+
sm: theme.components['button/size/sm/root/rest'],
|
|
295
|
+
},
|
|
296
|
+
variant: {
|
|
297
|
+
primary: theme.components['button/variant/primary/root/rest'],
|
|
298
|
+
secondary: theme.components['button/variant/secondary/root/rest'],
|
|
299
|
+
brand: theme.components['button/variant/brand/root/rest'],
|
|
300
|
+
},
|
|
301
|
+
disabled: { true: {} },
|
|
302
|
+
pressed: { true: {} },
|
|
303
|
+
},
|
|
304
|
+
compoundVariants: [
|
|
305
|
+
{
|
|
306
|
+
variant: 'primary',
|
|
307
|
+
pressed: true,
|
|
308
|
+
styles: theme.components['button/variant/primary/root/pressed'],
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
variant: 'primary',
|
|
312
|
+
disabled: true,
|
|
313
|
+
styles: theme.components['button/variant/primary/root/disabled'],
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
text: {
|
|
318
|
+
variants: {
|
|
319
|
+
variant: {
|
|
320
|
+
primary: theme.components['button/variant/primary/text/rest'],
|
|
321
|
+
secondary: theme.components['button/variant/secondary/text/rest'],
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
icon: {
|
|
326
|
+
variants: {
|
|
327
|
+
size: {
|
|
328
|
+
lg: theme.components['button/size/lg/icon/rest'],
|
|
329
|
+
md: theme.components['button/size/md/icon/rest'],
|
|
330
|
+
},
|
|
331
|
+
variant: {
|
|
332
|
+
primary: theme.components['button/variant/primary/icon/rest'],
|
|
333
|
+
secondary: theme.components['button/variant/secondary/icon/rest'],
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
}));
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
| Aspect | Web | Mobile |
|
|
341
|
+
| -------------- | ---------------------------------------- | ---------------------------------------------------- |
|
|
342
|
+
| Key format | camelCase (`buttonVariantRoot`) | Slash-separated (`button/variant/primary/root/rest`) |
|
|
343
|
+
| State handling | Separate class toggle | `compoundVariants` with pressed/disabled states |
|
|
344
|
+
| Multi-layer | Separate keys per layer (`Root`, `Icon`) | Nested style objects (`root`, `text`, `icon`) |
|
|
345
|
+
| Theme access | CSS variables in classes | Direct theme object reference |
|
|
346
|
+
|
|
347
|
+
### Generated Theme Shape
|
|
348
|
+
|
|
349
|
+
The theme exists in two forms:
|
|
350
|
+
|
|
351
|
+
1. **TypeScript types** (`generated/unistyles.d.ts`) - Generated by `scripts/generateTypes.ts` during package development. These provide full type safety for component development without needing actual runtime values. This is what allows IDE autocomplete and type checking in `styles.ts`.
|
|
352
|
+
|
|
353
|
+
2. **JavaScript runtime values** - Generated when consumers run `uds-mobile sync`. This creates the actual theme object with real color values, spacing numbers, etc. in their app (e.g., `lib/unistyles.uds.ts`).
|
|
354
|
+
|
|
355
|
+
This separation means we can develop type-safe components in the package without bundling theme values, while consumers get their own themed values at sync time.
|
|
356
|
+
|
|
357
|
+
Here's the theme shape (from the generated types):
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// Generated theme structure (simplified)
|
|
361
|
+
interface AppTheme {
|
|
362
|
+
// Foundation tokens
|
|
363
|
+
colors: {
|
|
364
|
+
background: { primary: string; secondary: string; brand: string /* ... */ };
|
|
365
|
+
foreground: { primary: string; secondary: string /* ... */ };
|
|
366
|
+
line: { primary: string; secondary: string /* ... */ };
|
|
367
|
+
spectrum: { 'gray-0': string; 'gray-1': string /* ... */ };
|
|
368
|
+
};
|
|
369
|
+
spacing: { '0': number; '1': number; '2': number /* ... */ };
|
|
370
|
+
borderRadius: { none: number; sm: number; md: number; lg: number; full: number };
|
|
371
|
+
borderWidth: { none: number; sm: number; md: number /* ... */ };
|
|
372
|
+
iconSize: { sm: number; md: number; lg: number /* ... */ };
|
|
373
|
+
typography: { 'body-md': TypographyVariant; 'heading-lg': TypographyVariant /* ... */ };
|
|
374
|
+
shadow: { sm: ShadowValue; md: ShadowValue /* ... */ };
|
|
375
|
+
font: { 'YahooSans-Bold': string; 'YahooSans-Regular': string /* ... */ };
|
|
376
|
+
|
|
377
|
+
// Component-specific styles (flat key structure)
|
|
378
|
+
components: {
|
|
379
|
+
'button/size/lg/root/rest': { gap: number; paddingHorizontal: number; paddingVertical: number };
|
|
380
|
+
'button/size/md/root/rest': { gap: number; paddingHorizontal: number; paddingVertical: number };
|
|
381
|
+
'button/variant/primary/root/rest': { backgroundColor: string /* ... */ };
|
|
382
|
+
'button/variant/primary/root/pressed': { backgroundColor: string /* ... */ };
|
|
383
|
+
'button/variant/primary/root/disabled': { opacity: number /* ... */ };
|
|
384
|
+
'button/variant/primary/text/rest': { color: string /* ... */ };
|
|
385
|
+
'button/variant/primary/icon/rest': { color: string /* ... */ };
|
|
386
|
+
// ... hundreds more component style entries
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### Component Key Format
|
|
392
|
+
|
|
393
|
+
Component styles use a slash-separated key format:
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
{component}/{dimension}/{value}/{layer}/{state}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
| Segment | Description | Examples |
|
|
400
|
+
| ----------- | ------------------------------------------------- | ---------------------------------- |
|
|
401
|
+
| `component` | Component name | `button`, `badge`, `avatar` |
|
|
402
|
+
| `dimension` | Variant dimension (size, variant, or nested type) | `size`, `variant`, `icon/variant` |
|
|
403
|
+
| `value` | Specific value for that dimension | `lg`, `md`, `primary`, `secondary` |
|
|
404
|
+
| `layer` | UI layer within the component | `root`, `text`, `icon`, `rootText` |
|
|
405
|
+
| `state` | Interaction state | `rest`, `pressed`, `disabled` |
|
|
406
|
+
|
|
407
|
+
Examples:
|
|
408
|
+
|
|
409
|
+
- `button/size/lg/root/rest` → Button, size=lg, root layer, rest state
|
|
410
|
+
- `button/variant/primary/icon/pressed` → Button, variant=primary, icon layer, pressed state
|
|
411
|
+
- `avatar/icon/variant/secondary/root/rest` → Avatar (icon type), variant=secondary, root layer
|
|
412
|
+
|
|
413
|
+
This flat structure allows `styles.ts` to reference exact theme values:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// In styles.ts
|
|
417
|
+
export const buttonStyles = StyleSheet.create((theme) => ({
|
|
418
|
+
root: {
|
|
419
|
+
variants: {
|
|
420
|
+
size: {
|
|
421
|
+
lg: theme.components['button/size/lg/root/rest'], // ← Direct lookup
|
|
422
|
+
md: theme.components['button/size/md/root/rest'],
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
}));
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### How Unistyles Variants Work
|
|
430
|
+
|
|
431
|
+
Unistyles variants allow styles to change based on props passed to `useVariants()`. The structure can be confusing at first because of the nesting, so here's a breakdown:
|
|
432
|
+
|
|
433
|
+
#### Basic Structure
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
const buttonStyles = StyleSheet.create((theme) => ({
|
|
437
|
+
// Each top-level key is a "layer" (root, text, icon, etc.)
|
|
438
|
+
root: {
|
|
439
|
+
// Base styles applied to ALL variants
|
|
440
|
+
flexDirection: 'row',
|
|
441
|
+
alignItems: 'center',
|
|
442
|
+
|
|
443
|
+
// Variants object defines conditional styles
|
|
444
|
+
variants: {
|
|
445
|
+
// Each key here is a "variant dimension" (size, variant, disabled, etc.)
|
|
446
|
+
size: {
|
|
447
|
+
// Each nested key is a possible value for that dimension
|
|
448
|
+
lg: { paddingHorizontal: 24, paddingVertical: 16 },
|
|
449
|
+
md: { paddingHorizontal: 16, paddingVertical: 12 },
|
|
450
|
+
sm: { paddingHorizontal: 12, paddingVertical: 8 },
|
|
451
|
+
},
|
|
452
|
+
variant: {
|
|
453
|
+
primary: { backgroundColor: theme.colors.background.brand },
|
|
454
|
+
secondary: { backgroundColor: theme.colors.background.secondary },
|
|
455
|
+
},
|
|
456
|
+
// Boolean variants use `true` as the key
|
|
457
|
+
disabled: {
|
|
458
|
+
true: { opacity: 0.5 },
|
|
459
|
+
},
|
|
460
|
+
pressed: {
|
|
461
|
+
true: {}, // Empty - handled by compoundVariants
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
}));
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
#### Using Variants in Components
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
function Button({ size = 'md', variant = 'primary', disabled }) {
|
|
472
|
+
const [pressed, setPressed] = useState(false);
|
|
473
|
+
|
|
474
|
+
// Call useVariants with current prop values
|
|
475
|
+
// This "selects" which variant styles to apply
|
|
476
|
+
buttonStyles.useVariants({
|
|
477
|
+
size, // 'lg' | 'md' | 'sm'
|
|
478
|
+
variant, // 'primary' | 'secondary' | ...
|
|
479
|
+
disabled, // true | false
|
|
480
|
+
pressed, // true | false
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
return (
|
|
484
|
+
<Pressable
|
|
485
|
+
style={buttonStyles.root} // Styles auto-update based on useVariants
|
|
486
|
+
onPressIn={() => setPressed(true)}
|
|
487
|
+
onPressOut={() => setPressed(false)}
|
|
488
|
+
>
|
|
489
|
+
<Text style={buttonStyles.text}>Press me</Text>
|
|
490
|
+
</Pressable>
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
#### Style Resolution
|
|
496
|
+
|
|
497
|
+
When `useVariants({ size: 'lg', variant: 'primary', disabled: false })` is called:
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
// Final styles for `buttonStyles.root` are merged in order:
|
|
501
|
+
{
|
|
502
|
+
// 1. Base styles (always applied)
|
|
503
|
+
flexDirection: 'row',
|
|
504
|
+
alignItems: 'center',
|
|
505
|
+
|
|
506
|
+
// 2. Size variant styles (size: 'lg')
|
|
507
|
+
paddingHorizontal: 24,
|
|
508
|
+
paddingVertical: 16,
|
|
509
|
+
|
|
510
|
+
// 3. Variant styles (variant: 'primary')
|
|
511
|
+
backgroundColor: theme.colors.background.brand,
|
|
512
|
+
|
|
513
|
+
// 4. Disabled styles (disabled: false → not applied)
|
|
514
|
+
// opacity: 0.5 ← skipped because disabled is false
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
#### Compound Variants
|
|
519
|
+
|
|
520
|
+
Compound variants apply styles when **multiple conditions are true simultaneously**. This is essential for state-specific styling that depends on the variant:
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
const buttonStyles = StyleSheet.create((theme) => ({
|
|
524
|
+
root: {
|
|
525
|
+
variants: {
|
|
526
|
+
variant: {
|
|
527
|
+
primary: { backgroundColor: theme.colors.background.brand },
|
|
528
|
+
secondary: { backgroundColor: theme.colors.background.secondary },
|
|
529
|
+
},
|
|
530
|
+
pressed: { true: {} }, // Empty placeholder - actual styles in compoundVariants
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
// Compound variants: apply when ALL specified conditions match
|
|
534
|
+
compoundVariants: [
|
|
535
|
+
{
|
|
536
|
+
variant: 'primary',
|
|
537
|
+
pressed: true,
|
|
538
|
+
styles: { backgroundColor: theme.colors.background.brandHover },
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
variant: 'secondary',
|
|
542
|
+
pressed: true,
|
|
543
|
+
styles: { backgroundColor: theme.colors.background.secondaryHover },
|
|
544
|
+
},
|
|
545
|
+
],
|
|
546
|
+
},
|
|
547
|
+
}));
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
**Why compound variants?** Each button variant needs a different pressed color. Without compound variants, you'd need a single `pressed: { true: { ... } }` style that works for all variants—impossible when different variants need different pressed colors.
|
|
551
|
+
|
|
552
|
+
#### Resolution Order
|
|
553
|
+
|
|
554
|
+
Styles are merged in this priority (later wins):
|
|
555
|
+
|
|
556
|
+
1. **Base styles** - Always applied
|
|
557
|
+
2. **Single variants** - Applied when their condition matches
|
|
558
|
+
3. **Compound variants** - Applied when ALL their conditions match (highest priority)
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
// With useVariants({ variant: 'primary', pressed: true }):
|
|
562
|
+
{
|
|
563
|
+
// 1. Base: flexDirection, alignItems
|
|
564
|
+
// 2. variant='primary': backgroundColor: brand
|
|
565
|
+
// 3. pressed=true: (empty)
|
|
566
|
+
// 4. COMPOUND (variant='primary' + pressed=true): backgroundColor: brandHover ← wins!
|
|
567
|
+
}
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### `generated/unistyles.d.ts`
|
|
571
|
+
|
|
572
|
+
Type declarations for Unistyles theme augmentation. Ensures TypeScript knows the shape of the theme object.
|
|
573
|
+
|
|
574
|
+
### `fonts/index.cjs` and `fonts/index.mjs`
|
|
575
|
+
|
|
576
|
+
Font manifests that export:
|
|
577
|
+
|
|
578
|
+
- `fonts`: Array of font file paths for Expo's font plugin
|
|
579
|
+
- `fontAliasToPostscript`: Mapping of font IDs to PostScript names (required by React Native)
|
|
580
|
+
|
|
581
|
+
## Code Generation Pipeline
|
|
582
|
+
|
|
583
|
+
The generation pipeline runs via `bun scripts/generate.ts` (or `bun run generate`):
|
|
584
|
+
|
|
585
|
+
```
|
|
586
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
587
|
+
│ generate.ts │
|
|
588
|
+
│ 1. generateTypes() → generated/unistyles.d.ts │
|
|
589
|
+
│ 2. generateStyles() → generated/styles.ts │
|
|
590
|
+
│ 3. generateFonts() → fonts/index.{cjs,mjs} │
|
|
591
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Step 1: generateTypes.ts
|
|
595
|
+
|
|
596
|
+
Uses [ts-morph](https://ts-morph.com/) to:
|
|
597
|
+
|
|
598
|
+
1. Load `@yahoo/uds/tokens` type definitions
|
|
599
|
+
2. Generate a temporary theme using `generateTheme()`
|
|
600
|
+
3. Infer TypeScript types from runtime values
|
|
601
|
+
4. Write `unistyles.d.ts` with proper theme augmentation
|
|
602
|
+
|
|
603
|
+
### Step 2: generateStyles.ts
|
|
604
|
+
|
|
605
|
+
The most complex generation step:
|
|
606
|
+
|
|
607
|
+
1. **Load TypeScript definitions** via ts-morph:
|
|
608
|
+
- Reads `StyleProps` interfaces from `@yahoo/uds/tokens/types.ts`
|
|
609
|
+
- Extracts all possible values for each prop (e.g., `backgroundColor: 'primary' | 'secondary' | ...`)
|
|
610
|
+
|
|
611
|
+
2. **Validate against React Native types**:
|
|
612
|
+
- Loads Unistyles' `AllAvailableStyles` type
|
|
613
|
+
- Filters out values not valid in React Native (e.g., `'grid'` for `display`)
|
|
614
|
+
|
|
615
|
+
3. **Map UDS props to theme paths**:
|
|
616
|
+
- Uses `themeConfig.ts` to map type names to theme locations
|
|
617
|
+
- Example: `BackgroundColor` type → `theme.colors.background`
|
|
618
|
+
|
|
619
|
+
4. **Generate component styles**:
|
|
620
|
+
- Uses `generateLayerBasedComponentStyles.ts` to create per-component stylesheets
|
|
621
|
+
- Reads component configs from `@yahoo/uds/tokens`
|
|
622
|
+
- Maps component keys like `button/size/lg/root/rest` to theme references
|
|
623
|
+
|
|
624
|
+
5. **Output formatted TypeScript**:
|
|
625
|
+
- Uses Prettier for consistent formatting
|
|
626
|
+
- Writes to `generated/styles.ts`
|
|
627
|
+
|
|
628
|
+
### Step 3: generateFonts.ts
|
|
629
|
+
|
|
630
|
+
1. Scans `fonts/` directory for `.otf` and `.ttf` files
|
|
631
|
+
2. Uses `fontkit` to extract PostScript names from font files
|
|
632
|
+
3. Generates CommonJS and ESM manifests for Expo compatibility
|
|
633
|
+
|
|
634
|
+
## Theme Generation
|
|
635
|
+
|
|
636
|
+
The CLI (`uds-mobile sync`) generates themes for consumer apps:
|
|
637
|
+
|
|
638
|
+
```
|
|
639
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
640
|
+
│ Consumer App │
|
|
641
|
+
│ │
|
|
642
|
+
│ uds.config.ts ──────► uds-mobile sync ──────► unistyles.uds.ts │
|
|
643
|
+
│ (UDS tokens) (CLI command) (theme objects) │
|
|
644
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### How `generateTheme.ts` works:
|
|
648
|
+
|
|
649
|
+
1. **Input**: `UniversalTokensConfig` from `@yahoo/uds/tokens`
|
|
650
|
+
|
|
651
|
+
2. **Color Processing**:
|
|
652
|
+
- Generates spectrum colors (hue + step combinations)
|
|
653
|
+
- Creates semantic colors (background, foreground, line)
|
|
654
|
+
- Handles `always/*` colors that don't change with theme
|
|
655
|
+
|
|
656
|
+
3. **Typography**:
|
|
657
|
+
- Converts CSS font-family + weight to PostScript names
|
|
658
|
+
- React Native requires PostScript names for custom fonts
|
|
659
|
+
|
|
660
|
+
4. **Component Styles**:
|
|
661
|
+
- Flattens component config into key-value pairs
|
|
662
|
+
- Keys use format: `component/variant/value/layer/state`
|
|
663
|
+
- Example: `button/variant/primary/root/pressed`
|
|
664
|
+
|
|
665
|
+
5. **Output**: Light and dark theme objects ready for Unistyles
|
|
666
|
+
|
|
667
|
+
### Config Assumptions & Workarounds
|
|
668
|
+
|
|
669
|
+
The theme generator makes assumptions about config structure. When configs don't honor these assumptions, workarounds are required. These are documented in `packages/mobile/docs/`.
|
|
670
|
+
|
|
671
|
+
#### Text-Only Props in Root Layers
|
|
672
|
+
|
|
673
|
+
**Problem:** React Native's `color` only works on `<Text>`, not `<View>`. Config defines `color` in root layers expecting CSS cascade behavior.
|
|
674
|
+
|
|
675
|
+
**Why it matters:** On web, CSS `color` on a parent cascades to child text. In RN, applying `color` to a `<View>` does nothing—text stays black. Without this workaround, 74 style definitions across 7 components would have broken text colors.
|
|
676
|
+
|
|
677
|
+
**Workaround:** The generator detects text-only props (`color`) in root layers and extracts them into a separate `rootText` layer, which components apply to their `<Text>` children:
|
|
678
|
+
|
|
679
|
+
```tsx
|
|
680
|
+
// Generated theme has both layers
|
|
681
|
+
theme.components['button/variant/primary/root/rest'] // backgroundColor, etc.
|
|
682
|
+
theme.components['button/variant/primary/rootText/rest'] // color (for Text)
|
|
683
|
+
|
|
684
|
+
// Component applies them separately
|
|
685
|
+
<Pressable style={buttonStyles.root}>
|
|
686
|
+
<Text style={buttonStyles.text}>{children}</Text> {/* Gets color */}
|
|
687
|
+
</Pressable>
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
**Affected:** 7 components, 74 root paths (avatar, badge, button, checkbox, chip, link, radio)
|
|
691
|
+
|
|
692
|
+
See: `docs/TEXT_ONLY_PROPS_WORKAROUND.md`
|
|
693
|
+
|
|
694
|
+
#### Missing Disabled States
|
|
695
|
+
|
|
696
|
+
**Problem:** Some components don't define `disabled` state in config but need disabled styling.
|
|
697
|
+
|
|
698
|
+
**Why it matters:** Users expect disabled components to look disabled. Without config-defined states, the generator can't output disabled styles, so components would look fully interactive even when `disabled={true}`.
|
|
699
|
+
|
|
700
|
+
**Workaround:** Manual `opacity: 0.5` in component files:
|
|
701
|
+
|
|
702
|
+
```tsx
|
|
703
|
+
<Pressable
|
|
704
|
+
disabled={disabled}
|
|
705
|
+
style={[styles.root, disabled && { opacity: 0.5 }]}
|
|
706
|
+
>
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
This is hardcoded in each component rather than generated from config.
|
|
710
|
+
|
|
711
|
+
**Affected:** radio, switch, chip, link, menu, iconButton
|
|
712
|
+
|
|
713
|
+
See: `docs/MISSING_DISABLED_STATES.md`
|
|
714
|
+
|
|
715
|
+
#### Always Palette Special Handling
|
|
716
|
+
|
|
717
|
+
**Problem:** `always/current` uses `currentColor` (CSS-only concept), and `alwaysPaletteAliases` is a separate type not in spectrum.
|
|
718
|
+
|
|
719
|
+
**Why it matters:** The `always/*` palette contains colors that don't change between light/dark themes (e.g., `always/light`, `always/dark`). But `always/current` maps to CSS `currentColor`—a concept that doesn't exist in React Native. If the generator tried to resolve it, it would crash or produce invalid styles.
|
|
720
|
+
|
|
721
|
+
**Workaround:**
|
|
722
|
+
|
|
723
|
+
```typescript
|
|
724
|
+
// In generateTheme.ts
|
|
725
|
+
if (value === 'always/current') {
|
|
726
|
+
return undefined; // Skip - no RN equivalent
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// alwaysPaletteAliases handled separately from spectrum colors
|
|
730
|
+
if (type === 'alwaysPaletteAliases') {
|
|
731
|
+
return theme.colors.always[value]; // Special lookup
|
|
732
|
+
}
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
See: `docs/ALWAYS_PALETTE_HANDLING.md`
|
|
736
|
+
|
|
737
|
+
#### Property Name Inconsistency (`iconSize` vs `size`)
|
|
738
|
+
|
|
739
|
+
**Problem:** Avatar uses `iconSize` but Button/Badge/Input/Switch use `size` for their icon layers—yet all have type `iconSizes`.
|
|
740
|
+
|
|
741
|
+
**Why it matters:** The generator maps config properties to React Native style properties. For icon fonts, we need `fontSize` and `lineHeight`. With inconsistent naming, we must handle **both** property names:
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
// In configToRNMappings.ts - both map to the same RN props
|
|
745
|
+
const propToStyleProps = {
|
|
746
|
+
iconSize: ['fontSize', 'lineHeight'], // Avatar uses this
|
|
747
|
+
size: ['fontSize', 'lineHeight'], // Everyone else uses this
|
|
748
|
+
};
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
Without this, Avatar icons wouldn't get proper sizing.
|
|
752
|
+
|
|
753
|
+
**Workaround:** Added `fontSize`/`lineHeight` mapping for **both** `iconSize` and `size` properties.
|
|
754
|
+
|
|
755
|
+
See: `docs/CONFIG_INCONSISTENCIES.md`
|
|
756
|
+
|
|
757
|
+
#### textStyle vs textVariant
|
|
758
|
+
|
|
759
|
+
**Problem:** Link config uses `textStyle` as variant name, but component exposes `textVariant` prop.
|
|
760
|
+
|
|
761
|
+
**Why it matters:** The generated styles use config variant names as keys. When the component prop name differs, you can't directly pass the prop value to the stylesheet:
|
|
762
|
+
|
|
763
|
+
```tsx
|
|
764
|
+
// Config defines: link/textStyle/body1/root
|
|
765
|
+
// Component receives: textVariant="body1"
|
|
766
|
+
|
|
767
|
+
// This doesn't work - key mismatch
|
|
768
|
+
linkStyles.useVariants({ textVariant }); // ❌ No 'textVariant' variant
|
|
769
|
+
|
|
770
|
+
// Must map prop to config key
|
|
771
|
+
linkStyles.useVariants({ textStyle: textVariant }); // ✅ Works
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
**Workaround:** Link component maps `textVariant` prop to `textStyle` variant key internally.
|
|
775
|
+
|
|
776
|
+
See: `docs/CONFIG_INCONSISTENCIES.md`
|
|
777
|
+
|
|
778
|
+
> **📋 Full documentation:** See `packages/mobile/docs/` for all workarounds and their proper fixes.
|
|
779
|
+
|
|
780
|
+
### Adding New Config Workarounds
|
|
781
|
+
|
|
782
|
+
When you encounter a new config inconsistency, here's where to add the fix:
|
|
783
|
+
|
|
784
|
+
| Workaround Type | File | Purpose |
|
|
785
|
+
| ----------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------- |
|
|
786
|
+
| **Property name mapping** | `scripts/utils/configToRNMappings.ts` | Map config prop names to RN style props (e.g., `iconSize` → `fontSize`) |
|
|
787
|
+
| **Text-only prop extraction** | `scripts/utils/configToRNMappings.ts` | `TEXT_ONLY_PROPS` set — props like `color` that must go on `<Text>`, not `<View>` |
|
|
788
|
+
| **Layer normalization** | `scripts/utils/generateComponentStyles.ts` | Transform layer names (e.g., `rootText` → `text`) |
|
|
789
|
+
| **Value resolution** | `bin/generateTheme.ts` | Handle special value types (e.g., `alwaysPaletteAliases`) |
|
|
790
|
+
| **Type mappings** | `scripts/utils/typesToThemePaths.ts` | Map UDS types to theme paths (e.g., `spacingAliases` → `theme.spacing`) |
|
|
791
|
+
| **Component-level** | `src/components/[Component].tsx` | Runtime fixes (e.g., manual `opacity: 0.5` for missing disabled states) |
|
|
792
|
+
|
|
793
|
+
#### Workflow for Adding a Workaround
|
|
794
|
+
|
|
795
|
+
1. **Identify the issue** - What's wrong with the config? Missing state, wrong property name, etc.
|
|
796
|
+
|
|
797
|
+
2. **Determine the fix location:**
|
|
798
|
+
- Can it be fixed in generation? → Use scripts/bin files
|
|
799
|
+
- Must be fixed at runtime? → Use component file
|
|
800
|
+
|
|
801
|
+
3. **Implement the fix** with clear comments:
|
|
802
|
+
|
|
803
|
+
```typescript
|
|
804
|
+
// In configToRNMappings.ts
|
|
805
|
+
// WORKAROUND: Avatar uses 'iconSize' but others use 'size'
|
|
806
|
+
// See: docs/CONFIG_INCONSISTENCIES.md
|
|
807
|
+
iconSize: ['fontSize', 'lineHeight'],
|
|
808
|
+
size: ['fontSize', 'lineHeight'],
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
4. **Document the workaround** in `packages/mobile/docs/`:
|
|
812
|
+
- Create a new `.md` file or update existing one
|
|
813
|
+
- Include: Problem, Why it matters, Workaround, Proper fix
|
|
814
|
+
|
|
815
|
+
5. **Regenerate styles** to verify:
|
|
816
|
+
|
|
817
|
+
```bash
|
|
818
|
+
cd packages/mobile
|
|
819
|
+
bun run generate
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
## Component Styling System
|
|
823
|
+
|
|
824
|
+
### Layer-Based Architecture
|
|
825
|
+
|
|
826
|
+
Components use a layer-based styling system:
|
|
827
|
+
|
|
828
|
+
```
|
|
829
|
+
Component
|
|
830
|
+
├── root # Container/pressable layer
|
|
831
|
+
├── text # Text content layer (rootText in configs)
|
|
832
|
+
├── icon # Icon layer
|
|
833
|
+
└── [custom] # Component-specific layers (e.g., checkbox, handle)
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### Variant Key Format
|
|
837
|
+
|
|
838
|
+
Component styles in the theme use this key format:
|
|
839
|
+
|
|
840
|
+
```
|
|
841
|
+
{component}/{dimension}/{value}/{layer}/{state}
|
|
842
|
+
|
|
843
|
+
Examples:
|
|
844
|
+
- button/size/lg/root/rest
|
|
845
|
+
- button/variant/primary/icon/pressed
|
|
846
|
+
- checkbox/variant/primary/value/checked/checkbox/disabled
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
### Using Component Styles
|
|
850
|
+
|
|
851
|
+
```typescript
|
|
852
|
+
// In a component
|
|
853
|
+
const { styles: rootStyles } = buttonStyles.useVariants({
|
|
854
|
+
size: 'md',
|
|
855
|
+
variant: 'primary',
|
|
856
|
+
disabled: isDisabled,
|
|
857
|
+
pressed: isPressed,
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
return (
|
|
861
|
+
<Pressable style={rootStyles.root}>
|
|
862
|
+
<Text style={rootStyles.text}>{children}</Text>
|
|
863
|
+
</Pressable>
|
|
864
|
+
);
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
## Unistyles Patterns
|
|
868
|
+
|
|
869
|
+
### Avoiding useUnistyles Re-renders
|
|
870
|
+
|
|
871
|
+
`useUnistyles()` hook causes component re-renders on theme changes. For better performance, use **dynamic functions** in stylesheets:
|
|
872
|
+
|
|
873
|
+
```tsx
|
|
874
|
+
// ❌ Bad: useUnistyles causes re-renders
|
|
875
|
+
import { useUnistyles } from 'react-native-unistyles';
|
|
876
|
+
|
|
877
|
+
function ColorSwatch({ colorKey }: { colorKey: string }) {
|
|
878
|
+
const { theme } = useUnistyles();
|
|
879
|
+
const backgroundColor = theme.colors.background[colorKey];
|
|
880
|
+
return <View style={{ backgroundColor }} />;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// ✅ Good: Dynamic function in stylesheet (no hook needed)
|
|
884
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
885
|
+
|
|
886
|
+
function ColorSwatch({ colorKey }: { colorKey: string }) {
|
|
887
|
+
return <View style={styles.swatch(colorKey)} />;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const styles = StyleSheet.create((theme) => ({
|
|
891
|
+
swatch: (colorKey: string) => ({
|
|
892
|
+
backgroundColor: theme.colors.background[colorKey],
|
|
893
|
+
width: 64,
|
|
894
|
+
height: 64,
|
|
895
|
+
}),
|
|
896
|
+
}));
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
Dynamic functions can accept multiple parameters:
|
|
900
|
+
|
|
901
|
+
```tsx
|
|
902
|
+
const styles = StyleSheet.create((theme) => ({
|
|
903
|
+
container: (maxWidth: number, isOdd: boolean) => ({
|
|
904
|
+
backgroundColor: theme.colors.background.primary,
|
|
905
|
+
maxWidth,
|
|
906
|
+
borderBottomWidth: isOdd ? 1 : undefined,
|
|
907
|
+
}),
|
|
908
|
+
}));
|
|
909
|
+
|
|
910
|
+
// Usage
|
|
911
|
+
<View style={styles.container(300, true)} />;
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
### When to Use Each Approach
|
|
915
|
+
|
|
916
|
+
| Approach | Use Case |
|
|
917
|
+
| ------------------ | ------------------------------------------- |
|
|
918
|
+
| Dynamic functions | Static theme access with parameters |
|
|
919
|
+
| `useAnimatedTheme` | Animated worklet access (zero re-renders) |
|
|
920
|
+
| `useUnistyles` | When you need runtime theme object in logic |
|
|
921
|
+
|
|
922
|
+
## Reanimated + Unistyles
|
|
923
|
+
|
|
924
|
+
When animating components, we use Reanimated with Unistyles. There are important patterns and gotchas to follow.
|
|
925
|
+
|
|
926
|
+
### ⚠️ Critical: Never Mix Styles Inside useAnimatedStyle
|
|
927
|
+
|
|
928
|
+
**Never spread Unistyles styles inside `useAnimatedStyle`:**
|
|
929
|
+
|
|
930
|
+
```tsx
|
|
931
|
+
// ❌ BAD: Spreading Unistyles inside useAnimatedStyle
|
|
932
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
933
|
+
...styles.container, // 💥 Never do this!
|
|
934
|
+
opacity: opacity.value,
|
|
935
|
+
}));
|
|
936
|
+
|
|
937
|
+
return <Animated.View style={animatedStyle} />;
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
**Why this breaks:** This creates a single style object with both Unistyles C++ state and Reanimated animation metadata. Both libraries try to update the same style nodes at the ShadowTree level, causing performance issues and visual bugs.
|
|
941
|
+
|
|
942
|
+
```tsx
|
|
943
|
+
// ✅ GOOD: Keep styles separate in array
|
|
944
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
945
|
+
opacity: opacity.value,
|
|
946
|
+
}));
|
|
947
|
+
|
|
948
|
+
return <Animated.View style={[styles.container, animatedStyle]} />;
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
> **📖 Unistyles docs:** [Reanimated Integration Guide](https://www.unistyl.es/v3/guides/reanimated#:~:text=new%20color%20automatically.-,Merging%20styles,-When%20you%20want)
|
|
952
|
+
|
|
953
|
+
### Animating Theme Colors
|
|
954
|
+
|
|
955
|
+
Use `useAnimatedVariantColor` for color properties:
|
|
956
|
+
|
|
957
|
+
```tsx
|
|
958
|
+
import { useAnimatedVariantColor } from 'react-native-unistyles/reanimated';
|
|
959
|
+
|
|
960
|
+
const backgroundColor = useAnimatedVariantColor(buttonStyles.root, 'backgroundColor');
|
|
961
|
+
const borderColor = useAnimatedVariantColor(buttonStyles.root, 'borderColor');
|
|
962
|
+
|
|
963
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
964
|
+
backgroundColor: withTiming(backgroundColor.value, { duration: 220 }),
|
|
965
|
+
borderColor: withTiming(borderColor.value, { duration: 220 }),
|
|
966
|
+
}));
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
### Animating Non-Color Theme Values
|
|
970
|
+
|
|
971
|
+
Use `useAnimatedTheme` for non-color properties (`boxShadow`, etc.):
|
|
972
|
+
|
|
973
|
+
```tsx
|
|
974
|
+
import { useAnimatedTheme } from 'react-native-unistyles/reanimated';
|
|
975
|
+
|
|
976
|
+
const animatedTheme = useAnimatedTheme(); // Zero re-renders!
|
|
977
|
+
|
|
978
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
979
|
+
const shadowPressed =
|
|
980
|
+
animatedTheme.value.components[`button/variant/${variant}/root/pressed`]?.boxShadow ?? '';
|
|
981
|
+
|
|
982
|
+
return {
|
|
983
|
+
boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value),
|
|
984
|
+
};
|
|
985
|
+
});
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
### State-Driven Animations
|
|
989
|
+
|
|
990
|
+
Prefer `useDerivedValue` over `useEffect` + `useSharedValue`:
|
|
991
|
+
|
|
992
|
+
```tsx
|
|
993
|
+
// ❌ Avoid: useEffect pattern
|
|
994
|
+
const progress = useSharedValue(0);
|
|
995
|
+
useEffect(() => {
|
|
996
|
+
progress.value = withTiming(visible ? 1 : 0);
|
|
997
|
+
}, [visible]);
|
|
998
|
+
|
|
999
|
+
// ✅ Prefer: useDerivedValue
|
|
1000
|
+
const progress = useDerivedValue(() => withTiming(visible ? 1 : 0), [visible]);
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
**Why:** No `useEffect` overhead, animation computed on UI thread, cleaner dependency tracking.
|
|
1004
|
+
|
|
1005
|
+
### Staggered Animations
|
|
1006
|
+
|
|
1007
|
+
Use `interpolate` with clamping for staggered effects:
|
|
1008
|
+
|
|
1009
|
+
```tsx
|
|
1010
|
+
const progress = useDerivedValue(() => withSpring(visible ? 1 : 0, SPRING_CONFIG), [visible]);
|
|
1011
|
+
|
|
1012
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
1013
|
+
// Width animates full range
|
|
1014
|
+
const width = interpolate(progress.value, [0, 1], [0, totalWidth]);
|
|
1015
|
+
|
|
1016
|
+
// Opacity/scale start at 50% progress - creates stagger effect
|
|
1017
|
+
const opacity = interpolate(progress.value, [0.5, 1], [0, 1], 'clamp');
|
|
1018
|
+
const scale = interpolate(progress.value, [0.5, 1], [0.7, 1], 'clamp');
|
|
1019
|
+
|
|
1020
|
+
return { width, opacity, transform: [{ scale }] };
|
|
1021
|
+
});
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
### Worklet Functions
|
|
1025
|
+
|
|
1026
|
+
Mark functions that run on the UI thread:
|
|
1027
|
+
|
|
1028
|
+
```tsx
|
|
1029
|
+
function interpolateShadowAlpha(shadow: string, alpha: number): string {
|
|
1030
|
+
'worklet'; // Required for UI thread execution
|
|
1031
|
+
if (!shadow || alpha >= 1) return shadow;
|
|
1032
|
+
if (alpha <= 0) return '';
|
|
1033
|
+
|
|
1034
|
+
return shadow.replace(/rgba\(([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)/g, (_, r, g, b, a) => {
|
|
1035
|
+
const newAlpha = parseFloat(a) * alpha;
|
|
1036
|
+
return `rgba(${r}, ${g}, ${b}, ${newAlpha.toFixed(3)})`;
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
### When to Use Each Hook
|
|
1042
|
+
|
|
1043
|
+
| Property Type | Hook | Example Properties |
|
|
1044
|
+
| ------------- | ------------------------- | ----------------------------------------- |
|
|
1045
|
+
| Colors | `useAnimatedVariantColor` | `backgroundColor`, `borderColor`, `color` |
|
|
1046
|
+
| Non-colors | `useAnimatedTheme` | `boxShadow`, `opacity`, custom values |
|
|
1047
|
+
| Computed | `useDerivedValue` | Progress values based on props/state |
|
|
1048
|
+
|
|
1049
|
+
## Development Workflow
|
|
1050
|
+
|
|
1051
|
+
### Initial Setup
|
|
1052
|
+
|
|
1053
|
+
```bash
|
|
1054
|
+
# From monorepo root
|
|
1055
|
+
bun install
|
|
1056
|
+
|
|
1057
|
+
# Build the package
|
|
1058
|
+
cd packages/mobile
|
|
1059
|
+
bun turbo build
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
### Making Changes
|
|
1063
|
+
|
|
1064
|
+
1. **Components** (`src/components/`): Edit directly, run `bun turbo dev` for watch mode
|
|
1065
|
+
2. **Styles generation**: Edit `scripts/generateStyles.ts`, then run `bun run generate`
|
|
1066
|
+
3. **Types**: Edit `scripts/generateTypes.ts`, then run `bun run generate`
|
|
1067
|
+
4. **Theme generation**: Edit `bin/generateTheme.ts`
|
|
1068
|
+
|
|
1069
|
+
### Testing Changes
|
|
1070
|
+
|
|
1071
|
+
```bash
|
|
1072
|
+
# Run tests
|
|
1073
|
+
bun turbo test
|
|
1074
|
+
|
|
1075
|
+
# Type check
|
|
1076
|
+
bun turbo typecheck
|
|
1077
|
+
|
|
1078
|
+
# Lint
|
|
1079
|
+
bun turbo lint
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
### Rebuilding Generated Files
|
|
1083
|
+
|
|
1084
|
+
```bash
|
|
1085
|
+
bun turbo build
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
## Adding New Components
|
|
1089
|
+
|
|
1090
|
+
### 1. Create the Component File
|
|
1091
|
+
|
|
1092
|
+
```typescript
|
|
1093
|
+
// src/components/MyComponent.tsx
|
|
1094
|
+
import { Pressable, type PressableProps } from 'react-native';
|
|
1095
|
+
import { myComponentStyles } from '../generated/styles';
|
|
1096
|
+
import { Text } from './Text';
|
|
1097
|
+
|
|
1098
|
+
export interface MyComponentProps extends PressableProps {
|
|
1099
|
+
size?: 'sm' | 'md' | 'lg';
|
|
1100
|
+
variant?: 'primary' | 'secondary';
|
|
1101
|
+
children: React.ReactNode;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
export function MyComponent({
|
|
1105
|
+
size = 'md',
|
|
1106
|
+
variant = 'primary',
|
|
1107
|
+
disabled,
|
|
1108
|
+
children,
|
|
1109
|
+
...props
|
|
1110
|
+
}: MyComponentProps) {
|
|
1111
|
+
const { styles } = myComponentStyles.useVariants({
|
|
1112
|
+
size,
|
|
1113
|
+
variant,
|
|
1114
|
+
disabled: !!disabled,
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
return (
|
|
1118
|
+
<Pressable style={styles.root} disabled={disabled} {...props}>
|
|
1119
|
+
<Text style={styles.text}>{children}</Text>
|
|
1120
|
+
</Pressable>
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
```
|
|
1124
|
+
|
|
1125
|
+
### 2. Export from Index
|
|
1126
|
+
|
|
1127
|
+
```typescript
|
|
1128
|
+
// src/index.ts
|
|
1129
|
+
export { MyComponent, type MyComponentProps } from './components/MyComponent';
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
### 3. Ensure Component Styles Exist
|
|
1133
|
+
|
|
1134
|
+
The component styles are generated from `@yahoo/uds/tokens` component configs. If the component doesn't exist in UDS tokens:
|
|
1135
|
+
|
|
1136
|
+
1. Add the component config to `@yahoo/uds`
|
|
1137
|
+
2. Run `bun run generate` to regenerate styles
|
|
1138
|
+
3. The component stylesheet will be auto-generated
|
|
1139
|
+
|
|
1140
|
+
### 4. Update Types (if needed)
|
|
1141
|
+
|
|
1142
|
+
Add prop types to `src/types.ts` if exposing new semantic types.
|
|
1143
|
+
|
|
1144
|
+
## Debugging
|
|
1145
|
+
|
|
1146
|
+
### Generated Styles Issues
|
|
1147
|
+
|
|
1148
|
+
1. Check `scripts/utils/themeConfig.ts` for type → theme path mappings
|
|
1149
|
+
2. Run `bun run generate` with verbose logging
|
|
1150
|
+
3. Check the console output for skipped props/values
|
|
1151
|
+
|
|
1152
|
+
### Component Styles Not Applying
|
|
1153
|
+
|
|
1154
|
+
1. Verify the component config exists in `@yahoo/uds/tokens`
|
|
1155
|
+
2. Check `generated/styles.ts` for the component stylesheet
|
|
1156
|
+
3. Ensure you're calling `useVariants()` with correct prop names
|
|
1157
|
+
|
|
1158
|
+
### Theme Mismatch
|
|
1159
|
+
|
|
1160
|
+
1. Regenerate the consumer app's theme: `uds-mobile sync`
|
|
1161
|
+
2. Clear Metro cache: `expo start --clear`
|
|
1162
|
+
3. Verify `uds.config.ts` matches the expected format
|
|
1163
|
+
|
|
1164
|
+
### Font Issues
|
|
1165
|
+
|
|
1166
|
+
1. Check `fonts/index.cjs` for the font mapping
|
|
1167
|
+
2. Verify PostScript names match what React Native expects
|
|
1168
|
+
3. Ensure fonts are properly linked in `app.config.ts`
|
|
1169
|
+
|
|
1170
|
+
## Questions?
|
|
1171
|
+
|
|
1172
|
+
- Check the [README.md](./README.md) for usage documentation
|
|
1173
|
+
- Review `docs/` for architecture decisions
|
|
1174
|
+
- Open an issue for bugs or feature requests
|