{"id":"l-rn-2-1","courseId":"react-native-apps","lang":"en","kind":"lesson","summary":"Style Trailhead with StyleSheet, style arrays and design tokens, using unitless points and longhand properties in a system with no cascade, selectors or media queries.","html":"<p>The first time you style a React Native screen, you reach for things that aren’t there: a class name, a <code dir=\"ltr\">:hover</code>, a media query, <code dir=\"ltr\">font-family</code> on <code dir=\"ltr\">body</code> so everything inherits it. React Native has none of them. What it has instead is smaller and more predictable: plain objects, applied directly to the component that needs them, in an order you control.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Style objects and StyleSheet.create</h2>\n<p>A style is a JavaScript object with camelCase keys. You can pass it inline, but most code collects styles with <code dir=\"ltr\">StyleSheet.create</code>, which gives you autocompletion, type checking and a single place to read a component’s styles:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">src/components/HikeCard.tsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { StyleSheet, Text, View } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react-native'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> HikeCard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">name</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">distanceKm</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#953800;--shiki-dark:#FFA657\">name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> string</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; </span><span style=\"color:#953800;--shiki-dark:#FFA657\">distanceKm</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">styles.card</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">styles.title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">styles.meta</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">distanceKm</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> km&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> styles</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> StyleSheet.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">create</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  card: {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    backgroundColor: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#ffffff'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    borderRadius: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    paddingVertical: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    paddingHorizontal: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    borderWidth: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    borderColor: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#e2e8e4'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  title: { fontSize: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">18</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, fontWeight: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'600'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, color: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#1d2a22'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  meta: { fontSize: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">14</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, color: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#5c6b62'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, marginTop: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">});</span></span></code></pre></div>\n<p>Defining <code dir=\"ltr\">styles</code> below the component is a convention, and a good one: the JSX comes first when you open the file.</p>\n<p>Is <code dir=\"ltr\">StyleSheet.create</code> faster than inline objects? Not in any way you’ll measure today. The real costs of inline objects are readability and new object identities on every render, which matter only when you pass styles into memoized children. A sensible default: static styles go in <code dir=\"ltr\">StyleSheet.create</code>, and the few values that depend on props or state go inline or in the style array next to them.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Numbers without units</h2>\n<p><code dir=\"ltr\">borderRadius: 12</code> means 12 <strong>density-independent points</strong>, not physical pixels. A 12-point corner looks the same size on a cheap Android phone and on an iPhone with three times the pixel density; the platform multiplies for you. Percentages work as strings (<code dir=\"ltr\">width: '50%'</code>), and that’s it: no <code dir=\"ltr\">px</code>, <code dir=\"ltr\">rem</code>, <code dir=\"ltr\">em</code> or <code dir=\"ltr\">vh</code>.</p>\n<p>Shorthands that take several values don’t exist either. Instead of <code dir=\"ltr\">margin: '8px 16px'</code> you write <code dir=\"ltr\">marginVertical: 8, marginHorizontal: 16</code>. The same pattern covers padding (<code dir=\"ltr\">paddingTop</code>, <code dir=\"ltr\">paddingHorizontal</code>), borders (<code dir=\"ltr\">borderTopWidth</code>, <code dir=\"ltr\">borderBottomColor</code>) and corners (<code dir=\"ltr\">borderTopLeftRadius</code>). Single-value shorthands like <code dir=\"ltr\">margin: 8</code> and <code dir=\"ltr\">borderRadius: 12</code> work as you’d expect.</p>\n<p>Modern React Native also supports several properties web developers miss: <code dir=\"ltr\">gap</code>, <code dir=\"ltr\">rowGap</code> and <code dir=\"ltr\">columnGap</code> for spacing children, <code dir=\"ltr\">boxShadow</code> with a CSS-like string, and <code dir=\"ltr\">filter</code>. Older tutorials use separate <code dir=\"ltr\">shadowColor</code>/<code dir=\"ltr\">shadowOffset</code> props for iOS and <code dir=\"ltr\">elevation</code> for Android, which you’ll still see in existing codebases.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Writing CSS strings</span></p>\n<p><code dir=\"ltr\">fontSize: '16px'</code> or <code dir=\"ltr\">margin: '8 16'</code> either throws a red-screen error or is silently ignored, depending on the property. If a style seems to do nothing, check for a unit or a multi-value string first. TypeScript catches most of these at the moment you type them, which is one more reason to keep it on.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">Style arrays: order is the only rule</h2>\n<p>The <code dir=\"ltr\">style</code> prop accepts an array. React Native merges it left to right, later keys win, and falsy entries (<code dir=\"ltr\">false</code>, <code dir=\"ltr\">null</code>, <code dir=\"ltr\">undefined</code>) are skipped. That turns conditional styling into one line:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">TSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[styles.card, isFavorite </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x26;&#x26;</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> styles.favorite, isSelected </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x26;&#x26;</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> styles.selected]</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span></code></pre></div>\n<p>There’s no specificity to reason about, no <code dir=\"ltr\">!important</code>, and no style arriving from a parent three files away. If a card looks wrong, the reason is in that array.</p>\n<p>Use the same rule to make your own components adjustable. Accept a <code dir=\"ltr\">style</code> prop and put it last:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">TSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { StyleProp, ViewStyle } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react-native'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Props</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#953800;--shiki-dark:#FFA657\">name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> string</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; </span><span style=\"color:#953800;--shiki-dark:#FFA657\">distanceKm</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; </span><span style=\"color:#953800;--shiki-dark:#FFA657\">style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?:</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> StyleProp</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#953800;--shiki-dark:#FFA657\">ViewStyle</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">> };</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> HikeCard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">name</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">distanceKm</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">style</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Props</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[styles.card, style]</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">      {</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">/* …title and meta as before */</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// A caller nudges one instance without forking the component:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">HikeCard</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Ridge Loop\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> distanceKm</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8.4</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ marginBottom: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">24</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span></code></pre></div>\n<p><code dir=\"ltr\">StyleProp&lt;ViewStyle&gt;</code> is the type that accepts an object, an array, or a falsy value, exactly like the built-in components.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Tokens instead of a cascade</h2>\n<p>Without inheritance, consistency comes from two places: components (one <code dir=\"ltr\">HikeCard</code>, one <code dir=\"ltr\">PrimaryButton</code>, one <code dir=\"ltr\">AppText</code>) and a small <strong>tokens</strong> file they all import.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">src/theme.ts</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> colors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  bg: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#f6f8f6'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  surface: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#ffffff'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  text: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#1d2a22'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  textMuted: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#5c6b62'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  primary: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#2f6f4e'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  border: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#e2e8e4'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">};</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> space</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { xs: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, sm: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, md: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, lg: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, xl: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">24</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> };</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> radius</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { sm: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, md: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, lg: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">20</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> };</span></span></code></pre></div>\n<p>Now the card reads <code dir=\"ltr\">borderRadius: radius.md</code> and <code dir=\"ltr\">paddingHorizontal: space.lg</code>, and a designer’s change lands in one file.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Dark mode with the same tokens</span></p>\n<p><code dir=\"ltr\">useColorScheme()</code> from <code dir=\"ltr\">react-native</code> returns <code dir=\"ltr\">'dark'</code> when the phone is in dark mode. Keep two token objects, pick one at the top of your tree, and hand it down through context; components never hard-code a color. You’ll build that context in section 3.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">Why no cascade is a feature</h2>\n<p>On a large web app, the hardest styling bugs come from rules you didn’t write reaching elements you didn’t expect. React Native removes that whole category. A component’s look is its own style plus whatever its caller passes in, and nothing else. It’s more typing at first, and much less debugging later. Teams that want class-like ergonomics add a library such as NativeWind (Tailwind classes compiled to style objects), but underneath it’s still these same objects.</p>\n<p>Next you’ll lay those styled boxes out with flexbox, where React Native’s defaults differ from the web in ways that explain most “why is this squashed?” moments.</p>\n","toc":[{"id":"sec-1","text":"Style objects and StyleSheet.create","level":2},{"id":"sec-2","text":"Numbers without units","level":2},{"id":"sec-3","text":"Style arrays: order is the only rule","level":2},{"id":"sec-4","text":"Tokens instead of a cascade","level":2},{"id":"sec-5","text":"Why no cascade is a feature","level":2}],"takeaways":["Styles are JavaScript objects with camelCase keys and unitless numbers measured in density-independent points.","CSS shorthands with several values, such as <code dir=\"ltr\">margin '8px 16px'</code>, don’t exist; use <code dir=\"ltr\">marginVertical</code> and <code dir=\"ltr\">marginHorizontal</code>.","In a style array later entries win and falsy entries are skipped, which makes conditional styles a one-liner.","Reuse happens through components and a tokens object, not through selectors or a global stylesheet.","Accept a <code dir=\"ltr\">style</code> prop and append it last so callers can adjust a component without forking it."],"further":[{"title":"Style","url":"https://reactnative.dev/docs/style"},{"title":"StyleSheet","url":"https://reactnative.dev/docs/stylesheet"},{"title":"useColorScheme","url":"https://reactnative.dev/docs/usecolorscheme"}],"quiz":[{"q":"<p>What renders for <code dir=\"ltr\">style={[styles.card, isFavorite &amp;&amp; styles.favorite, { padding: 8 }]}</code> when <code dir=\"ltr\">isFavorite</code> is <code dir=\"ltr\">false</code> and both styles set <code dir=\"ltr\">padding</code>?</p>","options":[{"text":"The app throws, because <code dir=\"ltr\">false</code> isn’t a valid style.","why":"Falsy entries in a style array are skipped on purpose; that’s what makes the <code dir=\"ltr\">&amp;&amp;</code> pattern safe here."},{"text":"<code dir=\"ltr\">styles.card</code>’s padding, because the first entry has priority.","why":"Priority goes the other way. Later entries override earlier ones, like <code dir=\"ltr\">Object.assign</code>."},{"text":"<code dir=\"ltr\">styles.favorite</code>’s padding, because named styles beat inline objects.","why":"There is no specificity in React Native. Order is the only rule, and <code dir=\"ltr\">favorite</code> was skipped anyway."},{"text":"A padding of 8, because the last entry in the array wins.","why":"Correct. <code dir=\"ltr\">false</code> is ignored, and the inline object comes last, so its <code dir=\"ltr\">padding</code> overrides the card’s."}],"answer":3},{"q":"<p>Which style object is valid React Native?</p>","options":[{"text":"<code dir=\"ltr\">{ margin: '8px 16px', fontSize: '16px' }</code>","why":"Multi-value shorthands and px strings are CSS syntax. React Native wants one number per property."},{"text":"<code dir=\"ltr\">{ marginVertical: 8, marginHorizontal: 16, fontSize: 16 }</code>","why":"Correct. Longhand keys with unitless numbers, measured in points."},{"text":"<code dir=\"ltr\">{ 'margin-vertical': 8, 'font-size': 16 }</code>","why":"Keys are camelCase JavaScript property names, not kebab-case CSS names."}],"answer":1},{"q":"<p>Trailhead needs every card to use the same corner radius and spacing. What’s the idiomatic approach?</p>","options":[{"text":"A <code dir=\"ltr\">theme.ts</code> file exporting tokens (colors, spacing, radius) that components import into their StyleSheets.","why":"Correct. Tokens give you one source of truth without needing a cascade, and they’re easy to swap for dark mode."},{"text":"A global stylesheet loaded in the root layout that targets every <code dir=\"ltr\">View</code>.","why":"React Native has no selectors, so there’s nothing that can target “every View”."},{"text":"Copying the numbers into each component and keeping them in sync by search and replace.","why":"It works until the designer changes the radius from 12 to 14 across forty files."}],"answer":0}],"exercise":{"kind":"web","mode":"js","starter":{"js":"// Merge a React Native style value into one plain object.\n// A style can be an object, a falsy value, or an array of styles (arrays can nest).\nfunction flattenStyle(style) {\n  if (Array.isArray(style)) {\n    return Object.assign(style[0], ...style.slice(1));\n  }\n  return style;\n}\n\nconst card = { padding: 12, borderRadius: 12 };\nconsole.log(flattenStyle([card, false, { padding: 8 }]));\n"},"solution":{"js":"function flattenStyle(style) {\n  if (!style) return {};\n  if (!Array.isArray(style)) return { ...style };\n  const result = {};\n  for (const entry of style) {\n    Object.assign(result, flattenStyle(entry));\n  }\n  return result;\n}\n\nconst card = { padding: 12, borderRadius: 12 };\nconsole.log(flattenStyle([card, false, { padding: 8 }]));\n"},"checks":[{"id":"single-object","test":"const eq = (a, b) => a !== null && typeof a === 'object' && JSON.stringify(Object.entries(a).sort()) === JSON.stringify(Object.entries(b).sort()); return typeof flattenStyle === 'function' && eq(flattenStyle({ padding: 4 }), { padding: 4 });"},{"id":"later-wins","test":"const eq = (a, b) => a !== null && typeof a === 'object' && JSON.stringify(Object.entries(a).sort()) === JSON.stringify(Object.entries(b).sort()); return eq(flattenStyle([{ padding: 12, color: 'green' }, { padding: 8 }]), { padding: 8, color: 'green' });"},{"id":"skips-falsy","test":"const eq = (a, b) => a !== null && typeof a === 'object' && JSON.stringify(Object.entries(a).sort()) === JSON.stringify(Object.entries(b).sort()); return eq(flattenStyle([{ a: 1 }, false, null, undefined, { b: 2 }]), { a: 1, b: 2 });"},{"id":"nested-arrays","test":"const eq = (a, b) => a !== null && typeof a === 'object' && JSON.stringify(Object.entries(a).sort()) === JSON.stringify(Object.entries(b).sort()); return eq(flattenStyle([{ a: 1 }, [{ b: 2 }, [false, { a: 3 }]]]), { a: 3, b: 2 });"},{"id":"no-mutation","test":"const base = { padding: 12 }; const extra = { padding: 8 }; flattenStyle([base, extra]); flattenStyle(base).padding = 99; return base.padding === 12 && extra.padding === 8;"},{"id":"falsy-input","test":"const eq = (a, b) => a !== null && typeof a === 'object' && JSON.stringify(Object.entries(a).sort()) === JSON.stringify(Object.entries(b).sort()); return eq(flattenStyle(null), {}) && eq(flattenStyle(false), {}) && eq(flattenStyle([]), {});"}],"title":"Write your own style flattener","prompt":"<p>React Native merges a <code dir=\"ltr\">style</code> prop the same way every time: arrays are read left to right, later keys win, falsy entries are skipped, and arrays can nest. Implement <code dir=\"ltr\">flattenStyle(style)</code> so it follows those rules and returns <strong>one new plain object</strong>.</p>\n<ul>\n<li>An object returns a copy of itself.</li>\n<li>A falsy value (<code dir=\"ltr\">false</code>, <code dir=\"ltr\">null</code>, <code dir=\"ltr\">undefined</code>) or an empty array returns <code dir=\"ltr\">{}</code>.</li>\n<li>It must never mutate the style objects you pass in.</li>\n</ul>\n<p>The starter is close but has three bugs. Find them.</p>\n","hints":["<code dir=\"ltr\">Object.assign(style[0], …)</code> writes into the first object. What happens to a shared <code dir=\"ltr\">styles.card</code> the next time it’s used?","Handle the simple cases first: falsy input returns <code dir=\"ltr\">{}</code>, and a non-array object returns <code dir=\"ltr\">{ ...style }</code>.","For arrays, start from <code dir=\"ltr\">const result = {}</code> and <code dir=\"ltr\">Object.assign(result, flattenStyle(entry))</code> for each entry; the recursive call takes care of nesting and falsy entries."],"explanation":"<p>The solution is recursive: every entry is flattened on its own, which handles nested arrays and falsy values with the same two base cases, and is merged into a fresh object so the caller’s styles are never touched. Mutating <code dir=\"ltr\">style[0]</code> is the dangerous bug: <code dir=\"ltr\">styles.card</code> is shared by every card, so one favorite card would quietly change all of them. This is the same contract as <code dir=\"ltr\">StyleSheet.flatten</code>, and knowing it lets you predict exactly what any style array will render.</p>\n","checkLabels":{"single-object":"A single object comes back with the same keys","later-wins":"Later entries override earlier ones","skips-falsy":"false, null and undefined entries are skipped","nested-arrays":"Nested arrays are flattened in order","no-mutation":"Input style objects are never modified","falsy-input":"Falsy input and empty arrays return {}"}},"words":705,"readMinutes":3}