diff --git a/apps/examples/src/App.js b/apps/examples/src/App.js
index 3cd1ca0..cd130de 100644
--- a/apps/examples/src/App.js
+++ b/apps/examples/src/App.js
@@ -185,31 +185,21 @@ export default function App(): React.MixedElement {
- {/* variables & themes */}
-
- Global variables
-
-
-
- Direct theming
-
-
-
-
- Inherit theming
-
-
-
-
-
- Nested theming
-
-
-
+
+
+ setAnimate(!animate)}>
+ {animate ? 'Reset' : 'Start'}
+
{/* block layout emulation */}
-
+
display:block emulation
@@ -253,8 +243,8 @@ export default function App(): React.MixedElement {
- {/* positioning (static by default) */}
-
+ {/* CSS positioning (static by default) */}
+
@@ -265,115 +255,16 @@ export default function App(): React.MixedElement {
- {/* visibility */}
-
-
-
-
-
+ {/* CSS text */}
+
+
+
+ line-height (unitless)
+
-
-
- {/* event emulation */}
-
- {
- console.log(e.type, e.target.value);
- }}
- onKeyDown={(e) => {
- console.log(e.type, e.key);
- }}
- onInput={(e) => {
- console.log(e.type, e.target.value);
- }}
- />
- {
- console.log(e.type, e.target.value);
- }}
- onKeyDown={(e) => {
- console.log(e.type, e.key);
- }}
- onInput={(e) => {
- console.log(e.type, e.target.value);
- }}
- />
- {
- setClickData((data) => ({
- color: data.color === 'red' ? 'blue' : 'red',
- text: 'click'
- }));
- setClickEventData({
- altKey: e.altKey,
- button: e.button,
- ctrlKey: e.ctrlKey,
- metaKey: e.metaKey,
- pageX: e.pageX,
- pageY: e.pageY,
- shiftKey: e.shiftKey
- });
- }}
- style={[styles.h100, styles.dynamicBg(clickData.color)]}
- >
- {clickData.text}
-
-
-
-
- {clickEventData.altKey ? '✅' : '🚫'} altKey
-
-
-
-
- {clickEventData.ctrlKey ? '✅' : '🚫'} ctrlKey
-
-
-
-
- {clickEventData.metaKey ? '✅' : '🚫'} metaKey
-
-
-
-
- {clickEventData.shiftKey ? '✅' : '🚫'} shiftKey
-
-
-
-
-
- button: {clickEventData.button}
-
-
- pageX: {clickEventData.pageX}
-
-
- pageY: {clickEventData.pageY}
-
-
-
+
+ line-height (em)
-
- {
- setImageLoadText(`${e.type}: loaded`);
- }}
- width={150}
- height={150}
- src="http://placehold.jp/150x150.png"
- style={styles.objContain}
- />
- {imageLoadText}
- {
- setImageErrorText(`${e.type}: errored`);
- }}
- width={150}
- height={150}
- src="http://error"
- style={styles.objContain}
- />
- {imageErrorText}
{/* CSS transitions shim */}
@@ -492,21 +383,145 @@ export default function App(): React.MixedElement {
Toggle
-
-
- setAnimate(!animate)}>
- {animate ? 'Reset' : 'Start'}
-
+
+ {/* visibility */}
+
+
+
+
+
+
+
+
+ {/* variables & themes */}
+
+ Global variables
+
+
+
+ Direct theming
+
+
+
+
+ Inherit theming
+
+
+
+
+
+ Nested theming
+
+
+
-
+
+ {/* hover */}
+
+
+ {/* event emulation */}
+
+ {
+ console.log(e.type, e.target.value);
+ }}
+ onKeyDown={(e) => {
+ console.log(e.type, e.key);
+ }}
+ onInput={(e) => {
+ console.log(e.type, e.target.value);
+ }}
+ />
+ {
+ console.log(e.type, e.target.value);
+ }}
+ onKeyDown={(e) => {
+ console.log(e.type, e.key);
+ }}
+ onInput={(e) => {
+ console.log(e.type, e.target.value);
+ }}
+ />
+ {
+ setClickData((data) => ({
+ color: data.color === 'red' ? 'blue' : 'red',
+ text: 'click'
+ }));
+ setClickEventData({
+ altKey: e.altKey,
+ button: e.button,
+ ctrlKey: e.ctrlKey,
+ metaKey: e.metaKey,
+ pageX: e.pageX,
+ pageY: e.pageY,
+ shiftKey: e.shiftKey
+ });
+ }}
+ style={[styles.h100, styles.dynamicBg(clickData.color)]}
+ >
+ {clickData.text}
+
+
+
+
+ {clickEventData.altKey ? '✅' : '🚫'} altKey
+
+
+
+
+ {clickEventData.ctrlKey ? '✅' : '🚫'} ctrlKey
+
+
+
+
+ {clickEventData.metaKey ? '✅' : '🚫'} metaKey
+
+
+
+
+ {clickEventData.shiftKey ? '✅' : '🚫'} shiftKey
+
+
+
+
+
+ button: {clickEventData.button}
+
+
+ pageX: {clickEventData.pageX}
+
+
+ pageY: {clickEventData.pageY}
+
+
+
+
+
+ {
+ setImageLoadText(`${e.type}: loaded`);
+ }}
+ width={150}
+ height={150}
+ src="http://placehold.jp/150x150.png"
+ style={styles.objContain}
+ />
+ {imageLoadText}
+ {
+ setImageErrorText(`${e.type}: errored`);
+ }}
+ width={150}
+ height={150}
+ src="http://error"
+ style={styles.objContain}
+ />
+ {imageErrorText}
+
);
@@ -671,5 +686,21 @@ const styles = css.create({
},
visibilityVisible: {
visibility: 'visible'
+ },
+ lineHeightUnitless: {
+ lineHeight: 2,
+ borderWidth: 1,
+ borderColor: 'black',
+ borderStyle: 'solid'
+ },
+ lineHeightEm: {
+ lineHeight: '2em',
+ borderWidth: 1,
+ borderColor: 'black',
+ borderStyle: 'solid'
+ },
+ text: {
+ fontSize: '2em',
+ backgroundColor: 'rgba(255,0,0,0.25)'
}
});
diff --git a/packages/react-strict-dom/src/native/stylex/index.js b/packages/react-strict-dom/src/native/stylex/index.js
index df5f188..4fc4b5a 100644
--- a/packages/react-strict-dom/src/native/stylex/index.js
+++ b/packages/react-strict-dom/src/native/stylex/index.js
@@ -220,7 +220,7 @@ function processStyle(style: S): S {
const propNames = Object.keys(result);
for (let i = 0; i < propNames.length; i++) {
const propName = propNames[i];
- let styleValue = result[propName];
+ const styleValue = result[propName];
if (
CSSMediaQuery.isMediaQueryString(propName) &&
@@ -242,19 +242,6 @@ function processStyle(style: S): S {
continue;
}
- // Polyfill unitless lineHeight
- // React Native treats unitless as a 'px' value
- // Web treats unitless as an 'em' value
- if (propName === 'lineHeight') {
- if (
- typeof styleValue === 'number' ||
- (typeof styleValue === 'string' &&
- CSSLengthUnitValue.parse(styleValue) == null)
- ) {
- styleValue = styleValue + 'em';
- }
- }
-
if (typeof styleValue === 'string') {
if (stringContainsVariables(styleValue)) {
result[propName] = CSSUnparsedValue.parse(propName, styleValue);
@@ -354,6 +341,34 @@ function resolveStyle(
continue;
}
+ // Polyfill unitless lineHeight
+ // React Native treats unitless as a 'px' value
+ // Web treats unitless as fontSize multiplier
+ if (propName === 'lineHeight') {
+ if (
+ typeof styleValue === 'number' ||
+ (typeof styleValue === 'string' &&
+ CSSLengthUnitValue.parse(styleValue) == null)
+ ) {
+ const lineHeightValue = parseFloat(styleValue);
+ // Only convert unitless lineHeight if fontSize exists
+ if (style.fontSize != null) {
+ if (style.fontSize instanceof CSSLengthUnitValue) {
+ const { value: fontSizeValue, unit: fontSizeUnit } = style.fontSize;
+ const value = new CSSLengthUnitValue(
+ lineHeightValue * fontSizeValue,
+ fontSizeUnit
+ );
+ stylesToReprocess[propName] = value;
+ result[propName] = value;
+ } else if (typeof style.fontSize === 'number') {
+ result[propName] = lineHeightValue * style.fontSize;
+ }
+ continue;
+ }
+ }
+ }
+
// resolve length units
if (styleValue instanceof CSSLengthUnitValue) {
result[propName] = styleValue.resolvePixelValue(options);
diff --git a/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native b/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native
index 28393d0..1fac9f5 100644
--- a/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native
+++ b/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native
@@ -272,6 +272,7 @@ exports[`properties: general line-height: rem 1`] = `
exports[`properties: general line-height: unitless number 1`] = `
{
"style": {
+ "fontSize": 16,
"lineHeight": 24,
},
}
@@ -280,6 +281,7 @@ exports[`properties: general line-height: unitless number 1`] = `
exports[`properties: general line-height: unitless string 1`] = `
{
"style": {
+ "fontSize": 16,
"lineHeight": 24,
},
}
diff --git a/packages/react-strict-dom/tests/__snapshots__/html-test.native.js.snap-native b/packages/react-strict-dom/tests/__snapshots__/html-test.native.js.snap-native
index 6025fcb..bcb78d8 100644
--- a/packages/react-strict-dom/tests/__snapshots__/html-test.native.js.snap-native
+++ b/packages/react-strict-dom/tests/__snapshots__/html-test.native.js.snap-native
@@ -1291,11 +1291,11 @@ exports[`html style polyfills default inherited text styles 1`] = `
"color": "red",
"direction": "rtl",
"fontFamily": "Arial",
- "fontSize": "1em",
+ "fontSize": 32,
"fontStyle": "italic",
"fontVariant": "variant",
"fontWeight": "300",
- "lineHeight": 1.6,
+ "lineHeight": 48,
"position": "static",
"textAlign": "right",
"textTransform": "uppercase",
@@ -1303,7 +1303,29 @@ exports[`html style polyfills default inherited text styles 1`] = `
}
}
>
- Text should inherit div styles
+ Text should
+
+ inherit
+
+ div styles
{
test('line-height', () => {
const styles = css.create({
numeric: {
+ fontSize: 16,
lineHeight: 1.5
},
string: {
+ fontSize: 16,
lineHeight: '1.5'
},
rem: {
diff --git a/packages/react-strict-dom/tests/html-test.native.js b/packages/react-strict-dom/tests/html-test.native.js
index bb9ced8..0e405a4 100644
--- a/packages/react-strict-dom/tests/html-test.native.js
+++ b/packages/react-strict-dom/tests/html-test.native.js
@@ -114,26 +114,39 @@ describe('html', () => {
});
test('default inherited text styles', () => {
- const inheritableStyles = {
- color: 'red',
- cursor: 'pointer',
- direction: 'rtl',
- fontFamily: 'Arial',
- fontSize: '1em',
- fontStyle: 'italic',
- fontVariant: 'variant',
- fontWeight: 'bold',
- letterSpace: '10px',
- lineHeight: 1.6,
- textAlign: 'right',
- textIndent: '10px',
- textTransform: 'uppercase',
- whiteSpace: ''
- };
+ const styles = css.create({
+ inheritable: {
+ color: 'red',
+ cursor: 'pointer',
+ direction: 'rtl',
+ fontFamily: 'Arial',
+ fontSize: '1em',
+ fontStyle: 'italic',
+ fontVariant: 'variant',
+ fontWeight: 'bold',
+ letterSpace: '10px',
+ lineHeight: 1.5,
+ textAlign: 'right',
+ textIndent: '10px',
+ textTransform: 'uppercase',
+ whiteSpace: 'pre'
+ },
+ alsoInheritable: {
+ fontWeight: 300
+ },
+ text: {
+ fontSize: '2em'
+ }
+ });
+ // This also tests that unitless line-height is correctly inherited, including
+ // through nested text elements.
const root = create(
-
-
- Text should inherit div styles
+
+
+
+ Text should inherit div
+ styles
+