diff --git a/apps/common-app/src/apps/css/examples/animations/routes.ts b/apps/common-app/src/apps/css/examples/animations/routes.ts
index d15150c9c654..bbcbcb3b690d 100644
--- a/apps/common-app/src/apps/css/examples/animations/routes.ts
+++ b/apps/common-app/src/apps/css/examples/animations/routes.ts
@@ -147,6 +147,11 @@ const layoutAndPositioningRoutes = {
name: 'Aspect Ratio',
Component: animatedProperties.layoutAndPositioning.others.AspectRatio,
},
+ BoxSizing: {
+ name: 'Box Sizing',
+ labelTypes: ['web'],
+ Component: animatedProperties.layoutAndPositioning.others.BoxSizing,
+ },
},
},
} satisfies Routes;
@@ -243,6 +248,23 @@ const appearanceRoutes = {
},
},
},
+ Outlines: {
+ name: 'Outlines',
+ routes: {
+ OutlineOffset: {
+ name: 'Outline Offset',
+ Component: animatedProperties.appearance.outlines.OutlineOffset,
+ },
+ OutlineStyle: {
+ name: 'Outline Style',
+ Component: animatedProperties.appearance.outlines.OutlineStyle,
+ },
+ OutlineWidth: {
+ name: 'Outline Width',
+ Component: animatedProperties.appearance.outlines.OutlineWidth,
+ },
+ },
+ },
Transforms: {
name: 'Transforms',
flatten: true,
@@ -311,6 +333,10 @@ const appearanceRoutes = {
name: 'Backface Visibility',
Component: animatedProperties.appearance.others.BackfaceVisibility,
},
+ MixBlendMode: {
+ name: 'Mix Blend Mode',
+ Component: animatedProperties.appearance.others.MixBlendMode,
+ },
},
},
} satisfies Routes;
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/colors/ColorProperties.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/colors/ColorProperties.tsx
index af18731dfb65..41a1d140cffd 100644
--- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/colors/ColorProperties.tsx
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/colors/ColorProperties.tsx
@@ -296,6 +296,26 @@ function OtherColors() {
shadowRadius: spacing.sm,
}}
/>
+
+ CardComponent={VerticalExampleCard}
+ buildAnimation={({ keyframes }) => ({
+ animationDirection: 'alternate',
+ animationDuration: '14s',
+ animationIterationCount: 'infinite',
+ animationName: keyframes,
+ animationTimingFunction: 'linear',
+ })}
+ renderExample={({ animation }) => (
+
+
+
+
+ )}
+ sections={[
+ {
+ examples: [
+ {
+ description:
+ '`mix-blend-mode` is a **continuous** property. That means, it **can be smoothly animated** between values.',
+ keyframes: {
+ '0%, 100%': {
+ mixBlendMode: 'normal',
+ },
+ '6.67%': {
+ mixBlendMode: 'multiply',
+ },
+ '13.33%': {
+ mixBlendMode: 'screen',
+ },
+ '20%': {
+ mixBlendMode: 'overlay',
+ },
+ '26.67%': {
+ mixBlendMode: 'darken',
+ },
+ '33.33%': {
+ mixBlendMode: 'lighten',
+ },
+ '40%': {
+ mixBlendMode: 'color-dodge',
+ },
+ '46.67%': {
+ mixBlendMode: 'color-burn',
+ },
+ '53.33%': {
+ mixBlendMode: 'hard-light',
+ },
+ '60%': {
+ mixBlendMode: 'soft-light',
+ },
+ '66.67%': {
+ mixBlendMode: 'difference',
+ },
+ '73.33%': {
+ mixBlendMode: 'exclusion',
+ },
+ '80%': {
+ mixBlendMode: 'hue',
+ },
+ '86.67%': {
+ mixBlendMode: 'saturation',
+ },
+ '93.33%': {
+ mixBlendMode: 'color',
+ },
+ '100%': {
+ mixBlendMode: 'luminosity',
+ },
+ },
+ minExampleHeight: 1.5 * sizes.xxxl,
+ title: 'Changing Mix Blend Mode',
+ },
+ ],
+ title: 'Mix Blend Mode',
+ },
+ ]}
+ />
+ );
+}
+
+const styles = StyleSheet.create({
+ box: {
+ backgroundColor: colors.primary,
+ borderRadius: radius.md,
+ height: sizes.xxxl,
+ position: 'absolute',
+ transform: [{ translateX: '-15%' }, { translateY: '-15%' }],
+ width: sizes.xxxl,
+ },
+ image: {
+ borderRadius: radius.md,
+ height: sizes.xxxl,
+ width: sizes.xxxl,
+ },
+});
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/index.ts b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/index.ts
index 5555398dc893..0091a6f14e46 100644
--- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/index.ts
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/index.ts
@@ -1,7 +1,9 @@
import BackfaceVisibility from './BackfaceVisibility';
+import MixBlendMode from './MixBlendMode';
import Opacity from './Opacity';
export default {
BackfaceVisibility,
+ MixBlendMode,
Opacity,
};
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineOffset.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineOffset.tsx
new file mode 100644
index 000000000000..2fb5fe04061b
--- /dev/null
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineOffset.tsx
@@ -0,0 +1,55 @@
+import { StyleSheet } from 'react-native';
+import type { CSSAnimationKeyframes } from 'react-native-reanimated';
+import Animated from 'react-native-reanimated';
+
+import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components';
+import { colors, radius, sizes, spacing } from '@/theme';
+
+export default function OutlineOffset() {
+ return (
+
+ CardComponent={VerticalExampleCard}
+ buildAnimation={({ keyframes }) => ({
+ animationDirection: 'alternate',
+ animationDuration: '1s',
+ animationIterationCount: 'infinite',
+ animationName: keyframes,
+ animationTimingFunction: 'linear',
+ })}
+ renderExample={({ animation }) => (
+
+ )}
+ sections={[
+ {
+ examples: [
+ {
+ keyframes: {
+ from: {
+ outlineOffset: 0,
+ },
+ to: {
+ outlineOffset: spacing.md,
+ },
+ },
+ title: 'Changing Outline Offset',
+ },
+ ],
+ title: 'Outline Offset',
+ },
+ ]}
+ />
+ );
+}
+
+const styles = StyleSheet.create({
+ box: {
+ backgroundColor: colors.primary,
+ borderColor: colors.primaryDark,
+ borderRadius: radius.sm,
+ height: sizes.xl,
+ outlineColor: colors.primaryDark,
+ outlineStyle: 'solid',
+ outlineWidth: spacing.xxs,
+ width: sizes.xl,
+ },
+});
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineStyle.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineStyle.tsx
new file mode 100644
index 000000000000..76e8654ae4cf
--- /dev/null
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineStyle.tsx
@@ -0,0 +1,58 @@
+import { StyleSheet } from 'react-native';
+import type { CSSAnimationKeyframes } from 'react-native-reanimated';
+import Animated from 'react-native-reanimated';
+
+import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components';
+import { colors, radius, sizes, spacing } from '@/theme';
+
+export default function OutlineStyle() {
+ return (
+
+ CardComponent={VerticalExampleCard}
+ buildAnimation={({ keyframes }) => ({
+ animationDuration: '3s',
+ animationIterationCount: 'infinite',
+ animationName: keyframes,
+ animationTimingFunction: 'linear',
+ })}
+ renderExample={({ animation }) => (
+
+ )}
+ sections={[
+ {
+ examples: [
+ {
+ description:
+ "`outlineStyle` is a **discrete** property. That means, it **can't be smoothly animated** between values. However, we can still change this property in the animation keyframes but the change will be **abrupt**.",
+ keyframes: {
+ '0%, 100%': {
+ outlineStyle: 'solid',
+ },
+ '33.3%': {
+ outlineStyle: 'dotted',
+ },
+ '66.6%': {
+ outlineStyle: 'dashed',
+ },
+ },
+ title: 'Changing Outline Style',
+ },
+ ],
+ title: 'Outline Style',
+ },
+ ]}
+ />
+ );
+}
+
+const styles = StyleSheet.create({
+ box: {
+ backgroundColor: colors.primary,
+ borderColor: colors.primaryDark,
+ borderRadius: radius.sm,
+ height: sizes.xl,
+ outlineColor: colors.primaryDark,
+ outlineWidth: spacing.xxs,
+ width: sizes.xl,
+ },
+});
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineWidth.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineWidth.tsx
new file mode 100644
index 000000000000..794291209cad
--- /dev/null
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineWidth.tsx
@@ -0,0 +1,55 @@
+import { StyleSheet } from 'react-native';
+import type { CSSAnimationKeyframes } from 'react-native-reanimated';
+import Animated from 'react-native-reanimated';
+
+import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components';
+import { colors, radius, sizes, spacing } from '@/theme';
+
+export default function OutlineWidth() {
+ return (
+
+ CardComponent={VerticalExampleCard}
+ buildAnimation={({ keyframes }) => ({
+ animationDirection: 'alternate',
+ animationDuration: '1s',
+ animationIterationCount: 'infinite',
+ animationName: keyframes,
+ animationTimingFunction: 'linear',
+ })}
+ renderExample={({ animation }) => (
+
+ )}
+ sections={[
+ {
+ examples: [
+ {
+ keyframes: {
+ from: {
+ outlineWidth: 0,
+ },
+ to: {
+ outlineWidth: spacing.md,
+ },
+ },
+ title: 'Changing Outline Width',
+ },
+ ],
+ title: 'Outline Width',
+ },
+ ]}
+ />
+ );
+}
+
+const styles = StyleSheet.create({
+ box: {
+ backgroundColor: colors.primary,
+ borderColor: colors.primaryDark,
+ borderRadius: radius.sm,
+ height: sizes.xl,
+ outlineColor: colors.primaryDark,
+ outlineStyle: 'solid',
+ outlineWidth: spacing.xxs,
+ width: sizes.xl,
+ },
+});
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/index.ts b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/index.ts
new file mode 100644
index 000000000000..103d169de429
--- /dev/null
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/index.ts
@@ -0,0 +1,5 @@
+import OutlineOffset from './OutlineOffset';
+import OutlineStyle from './OutlineStyle';
+import OutlineWidth from './OutlineWidth';
+
+export default { OutlineOffset, OutlineStyle, OutlineWidth };
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/BoxSizing.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/BoxSizing.tsx
new file mode 100644
index 000000000000..842ec0b7b3b0
--- /dev/null
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/BoxSizing.tsx
@@ -0,0 +1,63 @@
+import { StyleSheet, View } from 'react-native';
+import Animated from 'react-native-reanimated';
+
+import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components';
+import { colors, radius, sizes, spacing } from '@/theme';
+
+export default function BoxSizing() {
+ return (
+ ({
+ animationDuration: '1s',
+ animationIterationCount: 'infinite',
+ animationName: {
+ from: {
+ boxSizing: 'border-box',
+ },
+ to: {
+ boxSizing: 'content-box',
+ },
+ },
+ animationTimingFunction: 'linear',
+ })}
+ renderExample={({ animation }) => (
+
+
+
+ )}
+ sections={[
+ {
+ description:
+ "`boxSizing` is a **discrete** property. That means, it **can't be smoothly animated** between values. However, we can still change this property in the animation keyframes but the change will be **abrupt**.",
+ examples: [
+ {
+ // 'In this example, the component in the **foreground** is rendered inside of the component in the **background** with some offset applied. The part that is **outside** of the **background** component is **clipped**.',
+ title: 'Changing Box Sizing',
+ },
+ ],
+ labelTypes: ['web'],
+ title: 'Box Sizing',
+ },
+ ]}
+ />
+ );
+}
+
+const styles = StyleSheet.create({
+ child: {
+ borderColor: colors.primary,
+ height: sizes.lg,
+ width: '100%',
+ },
+ common: {
+ borderRadius: radius.md,
+ borderWidth: spacing.sm,
+ },
+ parent: {
+ backgroundColor: colors.primaryLight,
+ borderColor: colors.primaryDark,
+ height: sizes.xxl,
+ width: sizes.xxl,
+ },
+});
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/index.ts b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/index.ts
index dc54c41d36ca..3cac9198d976 100644
--- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/index.ts
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/index.ts
@@ -1,4 +1,5 @@
import AspectRatio from './AspectRatio';
+import BoxSizing from './BoxSizing';
import Display from './Display';
import Overflow from './Overflow';
import Position from './Position';
@@ -6,6 +7,7 @@ import ZIndex from './ZIndex';
export default {
AspectRatio,
+ BoxSizing,
Display,
Overflow,
Position,
diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/others/image/ResizeMode.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/others/image/ResizeMode.tsx
index 6a421ff5916f..fb8fe389cce2 100644
--- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/others/image/ResizeMode.tsx
+++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/others/image/ResizeMode.tsx
@@ -4,7 +4,7 @@ import Animated from 'react-native-reanimated';
import { balloonsImage } from '@/apps/css/assets';
import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components';
-import { colors, radius, sizes } from '@/theme';
+import { radius, sizes } from '@/theme';
export default function ResizeMode() {
return (
@@ -55,7 +55,6 @@ export default function ResizeMode() {
}
const styles = StyleSheet.create({
image: {
- backgroundColor: colors.primaryLight,
borderRadius: radius.md,
height: sizes.xxxl,
width: sizes.xxxl,
diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h
index 2d50a20ceecf..d3ffbe83fa93 100644
--- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h
+++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h
@@ -143,6 +143,7 @@ const InterpolatorFactoriesRecord PROPERTY_INTERPOLATORS_CONFIG = []() {
{"borderStartColor", value(BLACK)},
{"borderBlockColor", value(BLACK)},
// Other
+ {"outlineColor", value(BLACK)},
{"shadowColor", value(BLACK)},
{"overlayColor", value(BLACK)},
{"tintColor", value(BLACK)},
@@ -226,6 +227,11 @@ const InterpolatorFactoriesRecord PROPERTY_INTERPOLATORS_CONFIG = []() {
// Decoration
{"borderStyle", value("solid")},
+ // OUTLINES
+ {"outlineOffset", value(0)},
+ {"outlineStyle", value("solid")},
+ {"outlineWidth", value(0)},
+
// TRANSFORMS
{"transformOrigin",
array(
@@ -255,6 +261,7 @@ const InterpolatorFactoriesRecord PROPERTY_INTERPOLATORS_CONFIG = []() {
// OTHERS
{"backfaceVisibility", value("visible")},
{"opacity", value(1)},
+ {"mixBlendMode", value("normal")},
/**
* Typography
diff --git a/packages/react-native-reanimated/src/css/platform/native/config.ts b/packages/react-native-reanimated/src/css/platform/native/config.ts
index 4ddf2f5e1332..ace61000c471 100644
--- a/packages/react-native-reanimated/src/css/platform/native/config.ts
+++ b/packages/react-native-reanimated/src/css/platform/native/config.ts
@@ -102,7 +102,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = {
overflow: true,
zIndex: true,
aspectRatio: { process: processAspectRatio },
- boxSizing: false, // TODO
+ boxSizing: false, // web only
/** Appearance */
// COLORS
@@ -124,7 +124,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = {
borderStartColor: colorAttributes,
borderBlockColor: colorAttributes,
// Other
- outlineColor: false, // TODO
+ outlineColor: colorAttributes,
shadowColor: colorAttributes,
overlayColor: IS_ANDROID ? colorAttributes : false,
tintColor: colorAttributes,
@@ -177,9 +177,9 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = {
borderStyle: true,
// OUTLINES
- outlineOffset: false, // TODO
- outlineStyle: false, // TODO
- outlineWidth: false, // TODO
+ outlineOffset: true,
+ outlineStyle: true,
+ outlineWidth: true,
// TRANSFORMS
transformOrigin: { process: processTransformOrigin },
@@ -194,7 +194,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = {
// OTHERS
backfaceVisibility: true,
opacity: true,
- mixBlendMode: false, // TODO
+ mixBlendMode: true,
experimental_backgroundImage: false, // TODO
/** Typography */
diff --git a/packages/react-native-reanimated/src/css/platform/web/config.ts b/packages/react-native-reanimated/src/css/platform/web/config.ts
index 89b651c6156b..dd4ab4c264f7 100644
--- a/packages/react-native-reanimated/src/css/platform/web/config.ts
+++ b/packages/react-native-reanimated/src/css/platform/web/config.ts
@@ -103,7 +103,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = {
overflow: true,
zIndex: true,
aspectRatio: true,
- boxSizing: false, // TODO
+ boxSizing: true,
/** Appearance */
// COLORS
@@ -125,7 +125,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = {
borderStartColor: { as: 'borderLeftColor' },
borderBlockColor: colorAttributes,
// Other
- outlineColor: false, // TODO
+ outlineColor: colorAttributes,
shadowColor: boxShadowBuilder,
overlayColor: colorAttributes,
tintColor: colorAttributes,
@@ -178,9 +178,9 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = {
borderStyle: true,
// OUTLINES
- outlineOffset: false, // TODO
- outlineStyle: false, // TODO
- outlineWidth: false, // TODO
+ outlineOffset: 'px',
+ outlineStyle: true,
+ outlineWidth: 'px',
// TRANSFORMS
transformOrigin: { process: processTransformOrigin },
@@ -195,7 +195,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = {
// OTHERS
backfaceVisibility: true,
opacity: true,
- mixBlendMode: false, // TODO
+ mixBlendMode: true,
experimental_backgroundImage: false, // TODO
/** Typography */
diff --git a/packages/react-native-reanimated/src/propsAllowlists.ts b/packages/react-native-reanimated/src/propsAllowlists.ts
index 68c079cb450e..2e6d5a963f29 100644
--- a/packages/react-native-reanimated/src/propsAllowlists.ts
+++ b/packages/react-native-reanimated/src/propsAllowlists.ts
@@ -22,11 +22,18 @@ export const PropsAllowlists: AllowlistsHolder = {
/* ios styles */
shadowOpacity: true,
shadowRadius: true,
+ outlineColor: true,
+ shadowColor: true,
/* legacy android transform properties */
scaleX: true,
scaleY: true,
translateX: true,
translateY: true,
+ outlineStyle: true,
+ outlineWidth: true,
+ outlineOffset: true,
+ borderStyle: true,
+ mixBlendMode: true,
},
/**
* Whitelist of view props that can be updated in native thread via
@@ -123,7 +130,6 @@ export const PropsAllowlists: AllowlistsHolder = {
/* text color */
color: true,
tintColor: true,
- shadowColor: true,
textShadowColor: true,
placeholderTextColor: true,
textDecorationColor: true,