@mgcrea/react-native-tailwind 0.10.0 → 0.11.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.
Files changed (40) hide show
  1. package/README.md +30 -13
  2. package/dist/babel/config-loader.d.ts +12 -3
  3. package/dist/babel/config-loader.test.ts +14 -12
  4. package/dist/babel/config-loader.ts +41 -9
  5. package/dist/babel/index.cjs +50 -27
  6. package/dist/babel/plugin.d.ts +2 -1
  7. package/dist/babel/plugin.ts +11 -10
  8. package/dist/babel/utils/colorSchemeModifierProcessing.d.ts +3 -3
  9. package/dist/babel/utils/colorSchemeModifierProcessing.ts +4 -4
  10. package/dist/babel/utils/dynamicProcessing.d.ts +5 -5
  11. package/dist/babel/utils/dynamicProcessing.ts +11 -11
  12. package/dist/babel/utils/modifierProcessing.d.ts +3 -3
  13. package/dist/babel/utils/modifierProcessing.ts +5 -5
  14. package/dist/babel/utils/platformModifierProcessing.d.ts +3 -3
  15. package/dist/babel/utils/platformModifierProcessing.ts +4 -4
  16. package/dist/babel/utils/twProcessing.d.ts +3 -3
  17. package/dist/babel/utils/twProcessing.ts +6 -6
  18. package/dist/parser/index.d.ts +11 -4
  19. package/dist/parser/index.js +1 -1
  20. package/dist/parser/typography.d.ts +3 -1
  21. package/dist/parser/typography.js +1 -1
  22. package/dist/runtime.cjs +1 -1
  23. package/dist/runtime.cjs.map +3 -3
  24. package/dist/runtime.d.ts +8 -1
  25. package/dist/runtime.js +1 -1
  26. package/dist/runtime.js.map +3 -3
  27. package/dist/runtime.test.js +1 -1
  28. package/package.json +1 -1
  29. package/src/babel/config-loader.test.ts +14 -12
  30. package/src/babel/config-loader.ts +41 -9
  31. package/src/babel/plugin.ts +11 -10
  32. package/src/babel/utils/colorSchemeModifierProcessing.ts +4 -4
  33. package/src/babel/utils/dynamicProcessing.ts +11 -11
  34. package/src/babel/utils/modifierProcessing.ts +5 -5
  35. package/src/babel/utils/platformModifierProcessing.ts +4 -4
  36. package/src/babel/utils/twProcessing.ts +6 -6
  37. package/src/parser/index.ts +16 -8
  38. package/src/parser/typography.ts +14 -2
  39. package/src/runtime.test.ts +7 -7
  40. package/src/runtime.ts +37 -14
package/README.md CHANGED
@@ -425,7 +425,7 @@ export function RuntimeExample() {
425
425
 
426
426
  #### Configuration
427
427
 
428
- Configure custom colors and other theme options using `setConfig()`:
428
+ Configure custom colors and font families using `setConfig()`:
429
429
 
430
430
  ```typescript
431
431
  import { setConfig } from '@mgcrea/react-native-tailwind/runtime';
@@ -442,13 +442,17 @@ setConfig({
442
442
  dark: '#CC0000',
443
443
  },
444
444
  },
445
+ fontFamily: {
446
+ sans: ['"SF Pro Rounded"'],
447
+ custom: ['"My Custom Font"'],
448
+ },
445
449
  },
446
450
  },
447
451
  });
448
452
 
449
- // Now you can use custom colors
453
+ // Now you can use custom theme
450
454
  <View style={tw`bg-primary p-4`} />
451
- <Text style={tw`text-brand-light`}>Custom color</Text>
455
+ <Text style={tw`text-brand-light font-custom`}>Custom styling</Text>
452
456
  ```
453
457
 
454
458
  #### API Reference
@@ -1365,7 +1369,7 @@ This pattern allows you to build component libraries with optimized default styl
1365
1369
 
1366
1370
  **Available shades:** `50`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`
1367
1371
 
1368
- > **Note:** You can extend the color palette with custom colors via `tailwind.config.*` — see [Custom Colors](#custom-colors)
1372
+ > **Note:** You can extend the color palette with custom colors via `tailwind.config.*` — see [Custom Theme Extensions](#custom-theme-extensions)
1369
1373
 
1370
1374
  **Opacity Modifiers:**
1371
1375
 
@@ -1911,9 +1915,9 @@ Use arbitrary values for custom sizes, spacing, and borders not in the preset sc
1911
1915
 
1912
1916
  > **Note:** CSS units (`rem`, `em`, `vh`, `vw`) are not supported by React Native.
1913
1917
 
1914
- ### Custom Colors
1918
+ ### Custom Theme Extensions
1915
1919
 
1916
- Extend the default color palette via `tailwind.config.*` in your project root:
1920
+ Extend the default color palette and font families via `tailwind.config.*` in your project root:
1917
1921
 
1918
1922
  ```javascript
1919
1923
  // tailwind.config.mjs
@@ -1929,16 +1933,21 @@ export default {
1929
1933
  dark: "#0c4a6e",
1930
1934
  },
1931
1935
  },
1936
+ fontFamily: {
1937
+ sans: ['"SF Pro Rounded"'],
1938
+ custom: ['"My Custom Font"'],
1939
+ },
1932
1940
  },
1933
1941
  },
1934
1942
  };
1935
1943
  ```
1936
1944
 
1937
- Then use your custom colors:
1945
+ Then use your custom theme:
1938
1946
 
1939
1947
  ```tsx
1940
1948
  <View className="bg-primary p-4">
1941
- <Text className="text-brand">Custom branded text</Text>
1949
+ <Text className="text-brand font-custom">Custom branded text</Text>
1950
+ <Text className="font-sans">SF Pro Rounded text</Text>
1942
1951
  <View className="bg-brand-light rounded-lg" />
1943
1952
  </View>
1944
1953
  ```
@@ -1946,13 +1955,14 @@ Then use your custom colors:
1946
1955
  **How it works:**
1947
1956
 
1948
1957
  - Babel plugin discovers config by traversing up from source files
1949
- - Custom colors merged with defaults at build time (custom takes precedence)
1950
- - Nested objects flattened with dash notation: `brand.light` → `brand-light`
1958
+ - Custom theme merged with defaults at build time (custom takes precedence)
1959
+ - Nested color objects flattened with dash notation: `brand.light` → `brand-light`
1960
+ - Font families use first font in array (React Native doesn't support font stacks)
1951
1961
  - Zero runtime overhead — all loading happens during compilation
1952
1962
 
1953
1963
  **Supported formats:** `.js`, `.mjs`, `.cjs`, `.ts`
1954
1964
 
1955
- > **Tip:** Use `theme.extend.colors` to keep default Tailwind colors. Using `theme.colors` directly will override all defaults.
1965
+ > **Tip:** Use `theme.extend.*` to keep defaults. Using `theme.colors` or `theme.fontFamily` directly will override all defaults.
1956
1966
 
1957
1967
  ### Programmatic API
1958
1968
 
@@ -1961,10 +1971,17 @@ Access the parser and constants programmatically:
1961
1971
  ```typescript
1962
1972
  import { parseClassName, COLORS, SPACING_SCALE } from "@mgcrea/react-native-tailwind";
1963
1973
 
1964
- // Parse className strings
1965
- const _twStyles = parseClassName("m-4 p-2 bg-blue-500");
1974
+ // Parse className strings (no custom theme)
1975
+ const styles = parseClassName("m-4 p-2 bg-blue-500");
1966
1976
  // Returns: { margin: 16, padding: 8, backgroundColor: '#3B82F6' }
1967
1977
 
1978
+ // Parse with custom theme
1979
+ const customStyles = parseClassName("m-4 bg-primary font-custom", {
1980
+ colors: { primary: "#1d4ed8" },
1981
+ fontFamily: { custom: "My Custom Font" },
1982
+ });
1983
+ // Returns: { margin: 16, backgroundColor: '#1d4ed8', fontFamily: 'My Custom Font' }
1984
+
1968
1985
  // Access default scales
1969
1986
  const blueColor = COLORS["blue-500"]; // '#3B82F6'
1970
1987
  const spacing = SPACING_SCALE[4]; // 16
@@ -6,8 +6,10 @@ export type TailwindConfig = {
6
6
  theme?: {
7
7
  extend?: {
8
8
  colors?: Record<string, string | Record<string, string>>;
9
+ fontFamily?: Record<string, string | string[]>;
9
10
  };
10
11
  colors?: Record<string, string | Record<string, string>>;
12
+ fontFamily?: Record<string, string | string[]>;
11
13
  };
12
14
  };
13
15
  /**
@@ -19,7 +21,14 @@ export declare function findTailwindConfig(startDir: string): string | null;
19
21
  */
20
22
  export declare function loadTailwindConfig(configPath: string): TailwindConfig | null;
21
23
  /**
22
- * Extract custom colors from tailwind config
23
- * Prefers theme.extend.colors over theme.colors to avoid overriding defaults
24
+ * Custom theme configuration extracted from tailwind.config
24
25
  */
25
- export declare function extractCustomColors(filename: string): Record<string, string>;
26
+ export type CustomTheme = {
27
+ colors: Record<string, string>;
28
+ fontFamily: Record<string, string>;
29
+ };
30
+ /**
31
+ * Extract all custom theme extensions from tailwind config
32
+ * Prefers theme.extend.* over theme.* to avoid overriding defaults
33
+ */
34
+ export declare function extractCustomTheme(filename: string): CustomTheme;
@@ -1,6 +1,6 @@
1
1
  import * as fs from "fs";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
- import { extractCustomColors, findTailwindConfig, loadTailwindConfig } from "./config-loader";
3
+ import { extractCustomTheme, findTailwindConfig, loadTailwindConfig } from "./config-loader";
4
4
 
5
5
  // Mock fs
6
6
  vi.mock("fs");
@@ -115,15 +115,15 @@ describe("config-loader", () => {
115
115
  });
116
116
  });
117
117
 
118
- describe("extractCustomColors", () => {
119
- it("should return empty object when no config found", () => {
118
+ describe("extractCustomTheme", () => {
119
+ it("should return empty theme when no config found", () => {
120
120
  vi.spyOn(fs, "existsSync").mockReturnValue(false);
121
121
 
122
- const result = extractCustomColors("/project/src/file.ts");
123
- expect(result).toEqual({});
122
+ const result = extractCustomTheme("/project/src/file.ts");
123
+ expect(result).toEqual({ colors: {}, fontFamily: {} });
124
124
  });
125
125
 
126
- it("should return empty object when config has no theme", () => {
126
+ it("should return empty theme when config has no theme", () => {
127
127
  const configPath = "/project/tailwind.config.js";
128
128
 
129
129
  vi.spyOn(fs, "existsSync").mockImplementation((filepath) => filepath === configPath);
@@ -131,22 +131,24 @@ describe("config-loader", () => {
131
131
 
132
132
  // loadTailwindConfig will be called, but we've already tested it
133
133
  // For integration, we'd need to mock the entire flow
134
- const result = extractCustomColors("/project/src/file.ts");
134
+ const result = extractCustomTheme("/project/src/file.ts");
135
135
 
136
136
  // Without actual config loading, this returns empty
137
- expect(result).toEqual({});
137
+ expect(result).toEqual({ colors: {}, fontFamily: {} });
138
138
  });
139
139
 
140
- it("should extract colors from theme.extend.colors", () => {
140
+ it("should extract colors and fontFamily from theme.extend", () => {
141
141
  // This would require complex mocking of the entire require flow
142
- // Testing the logic: theme.extend.colors is preferred
142
+ // Testing the logic: theme.extend is preferred
143
143
  const colors = { brand: { light: "#fff", dark: "#000" } };
144
+ const fontFamily = { sans: ['"SF Pro"'], custom: ['"Custom Font"'] };
144
145
  const theme = {
145
- extend: { colors },
146
+ extend: { colors, fontFamily },
146
147
  };
147
148
 
148
- // If we had the config, we'd flatten the colors
149
+ // If we had the config, we'd flatten the colors and convert fontFamily
149
150
  expect(theme.extend.colors).toEqual(colors);
151
+ expect(theme.extend.fontFamily).toEqual(fontFamily);
150
152
  });
151
153
  });
152
154
  });
@@ -13,8 +13,10 @@ export type TailwindConfig = {
13
13
  theme?: {
14
14
  extend?: {
15
15
  colors?: Record<string, string | Record<string, string>>;
16
+ fontFamily?: Record<string, string | string[]>;
16
17
  };
17
18
  colors?: Record<string, string | Record<string, string>>;
19
+ fontFamily?: Record<string, string | string[]>;
18
20
  };
19
21
  };
20
22
 
@@ -82,23 +84,31 @@ export function loadTailwindConfig(configPath: string): TailwindConfig | null {
82
84
  }
83
85
 
84
86
  /**
85
- * Extract custom colors from tailwind config
86
- * Prefers theme.extend.colors over theme.colors to avoid overriding defaults
87
+ * Custom theme configuration extracted from tailwind.config
87
88
  */
88
- export function extractCustomColors(filename: string): Record<string, string> {
89
+ export type CustomTheme = {
90
+ colors: Record<string, string>;
91
+ fontFamily: Record<string, string>;
92
+ };
93
+
94
+ /**
95
+ * Extract all custom theme extensions from tailwind config
96
+ * Prefers theme.extend.* over theme.* to avoid overriding defaults
97
+ */
98
+ export function extractCustomTheme(filename: string): CustomTheme {
89
99
  const projectDir = path.dirname(filename);
90
100
  const configPath = findTailwindConfig(projectDir);
91
101
 
92
102
  if (!configPath) {
93
- return {};
103
+ return { colors: {}, fontFamily: {} };
94
104
  }
95
105
 
96
106
  const config = loadTailwindConfig(configPath);
97
107
  if (!config?.theme) {
98
- return {};
108
+ return { colors: {}, fontFamily: {} };
99
109
  }
100
110
 
101
- // Warn if using theme.colors instead of theme.extend.colors
111
+ // Extract colors
102
112
  /* v8 ignore next 5 */
103
113
  if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
104
114
  console.warn(
@@ -106,9 +116,31 @@ export function extractCustomColors(filename: string): Record<string, string> {
106
116
  "Use theme.extend.colors to add custom colors while keeping defaults.",
107
117
  );
108
118
  }
109
-
110
- // Prefer theme.extend.colors
111
119
  const colors = config.theme.extend?.colors ?? config.theme.colors ?? {};
112
120
 
113
- return flattenColors(colors);
121
+ // Extract fontFamily
122
+ /* v8 ignore next 5 */
123
+ if (config.theme.fontFamily && !config.theme.extend?.fontFamily && process.env.NODE_ENV !== "production") {
124
+ console.warn(
125
+ "[react-native-tailwind] Using theme.fontFamily will override all default font families. " +
126
+ "Use theme.extend.fontFamily to add custom fonts while keeping defaults.",
127
+ );
128
+ }
129
+ const fontFamily = config.theme.extend?.fontFamily ?? config.theme.fontFamily ?? {};
130
+
131
+ // Convert fontFamily values to strings (take first value if array)
132
+ const fontFamilyResult: Record<string, string> = {};
133
+ for (const [key, value] of Object.entries(fontFamily)) {
134
+ if (Array.isArray(value)) {
135
+ // Take first font in the array (React Native doesn't support font stacks)
136
+ fontFamilyResult[key] = value[0];
137
+ } else {
138
+ fontFamilyResult[key] = value;
139
+ }
140
+ }
141
+
142
+ return {
143
+ colors: flattenColors(colors),
144
+ fontFamily: fontFamilyResult,
145
+ };
114
146
  }
@@ -1678,7 +1678,13 @@ function parseArbitraryLineHeight(value) {
1678
1678
  }
1679
1679
  return null;
1680
1680
  }
1681
- function parseTypography(cls) {
1681
+ function parseTypography(cls, customFontFamily) {
1682
+ const fontFamilyMap = customFontFamily ? {
1683
+ ...FONT_FAMILY_MAP,
1684
+ ...Object.fromEntries(
1685
+ Object.entries(customFontFamily).map(([key, value]) => [`font-${key}`, { fontFamily: value }])
1686
+ )
1687
+ } : FONT_FAMILY_MAP;
1682
1688
  if (cls.startsWith("text-")) {
1683
1689
  const sizeKey = cls.substring(5);
1684
1690
  const arbitraryValue = parseArbitraryFontSize(sizeKey);
@@ -1701,7 +1707,7 @@ function parseTypography(cls) {
1701
1707
  return { lineHeight };
1702
1708
  }
1703
1709
  }
1704
- return FONT_FAMILY_MAP[cls] ?? FONT_WEIGHT_MAP[cls] ?? FONT_STYLE_MAP[cls] ?? TEXT_ALIGN_MAP[cls] ?? TEXT_DECORATION_MAP[cls] ?? TEXT_TRANSFORM_MAP[cls] ?? LINE_HEIGHT_MAP[cls] ?? TRACKING_MAP[cls] ?? null;
1710
+ return fontFamilyMap[cls] ?? FONT_WEIGHT_MAP[cls] ?? FONT_STYLE_MAP[cls] ?? TEXT_ALIGN_MAP[cls] ?? TEXT_DECORATION_MAP[cls] ?? TEXT_TRANSFORM_MAP[cls] ?? LINE_HEIGHT_MAP[cls] ?? TRACKING_MAP[cls] ?? null;
1705
1711
  }
1706
1712
 
1707
1713
  // src/parser/placeholder.ts
@@ -1842,22 +1848,22 @@ function splitModifierClasses(className) {
1842
1848
  }
1843
1849
 
1844
1850
  // src/parser/index.ts
1845
- function parseClassName(className, customColors) {
1851
+ function parseClassName(className, customTheme) {
1846
1852
  const classes = className.split(/\s+/).filter(Boolean);
1847
1853
  const style = {};
1848
1854
  for (const cls of classes) {
1849
- const parsedStyle = parseClass(cls, customColors);
1855
+ const parsedStyle = parseClass(cls, customTheme);
1850
1856
  Object.assign(style, parsedStyle);
1851
1857
  }
1852
1858
  return style;
1853
1859
  }
1854
- function parseClass(cls, customColors) {
1860
+ function parseClass(cls, customTheme) {
1855
1861
  const parsers = [
1856
1862
  parseSpacing,
1857
1863
  parseBorder,
1858
- (cls2) => parseColor(cls2, customColors),
1864
+ (cls2) => parseColor(cls2, customTheme?.colors),
1859
1865
  parseLayout,
1860
- parseTypography,
1866
+ (cls2) => parseTypography(cls2, customTheme?.fontFamily),
1861
1867
  parseSizing,
1862
1868
  parseShadow,
1863
1869
  parseAspectRatio,
@@ -1925,15 +1931,15 @@ function loadTailwindConfig(configPath) {
1925
1931
  return null;
1926
1932
  }
1927
1933
  }
1928
- function extractCustomColors(filename) {
1934
+ function extractCustomTheme(filename) {
1929
1935
  const projectDir = path.dirname(filename);
1930
1936
  const configPath = findTailwindConfig(projectDir);
1931
1937
  if (!configPath) {
1932
- return {};
1938
+ return { colors: {}, fontFamily: {} };
1933
1939
  }
1934
1940
  const config = loadTailwindConfig(configPath);
1935
1941
  if (!config?.theme) {
1936
- return {};
1942
+ return { colors: {}, fontFamily: {} };
1937
1943
  }
1938
1944
  if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
1939
1945
  console.warn(
@@ -1941,7 +1947,24 @@ function extractCustomColors(filename) {
1941
1947
  );
1942
1948
  }
1943
1949
  const colors = config.theme.extend?.colors ?? config.theme.colors ?? {};
1944
- return flattenColors(colors);
1950
+ if (config.theme.fontFamily && !config.theme.extend?.fontFamily && process.env.NODE_ENV !== "production") {
1951
+ console.warn(
1952
+ "[react-native-tailwind] Using theme.fontFamily will override all default font families. Use theme.extend.fontFamily to add custom fonts while keeping defaults."
1953
+ );
1954
+ }
1955
+ const fontFamily = config.theme.extend?.fontFamily ?? config.theme.fontFamily ?? {};
1956
+ const fontFamilyResult = {};
1957
+ for (const [key, value] of Object.entries(fontFamily)) {
1958
+ if (Array.isArray(value)) {
1959
+ fontFamilyResult[key] = value[0];
1960
+ } else {
1961
+ fontFamilyResult[key] = value;
1962
+ }
1963
+ }
1964
+ return {
1965
+ colors: flattenColors(colors),
1966
+ fontFamily: fontFamilyResult
1967
+ };
1945
1968
  }
1946
1969
 
1947
1970
  // src/babel/utils/attributeMatchers.ts
@@ -1997,7 +2020,7 @@ function processColorSchemeModifiers(colorSchemeModifiers, state, parseClassName
1997
2020
  const conditionalExpressions = [];
1998
2021
  for (const [scheme, modifiers] of modifiersByScheme) {
1999
2022
  const classNames = modifiers.map((m) => m.baseClass).join(" ");
2000
- const styleObject = parseClassName2(classNames, state.customColors);
2023
+ const styleObject = parseClassName2(classNames, state.customTheme);
2001
2024
  const styleKey = generateStyleKey2(`${scheme}_${classNames}`);
2002
2025
  state.styleRegistry.set(styleKey, styleObject);
2003
2026
  const colorSchemeCheck = t.binaryExpression(
@@ -2252,7 +2275,7 @@ function processStringOrExpressionHelper(node, state, parseClassName2, generateS
2252
2275
  if (isSchemeModifier2(modifier.modifier)) {
2253
2276
  const expanded = expandSchemeModifier2(
2254
2277
  modifier,
2255
- state.customColors,
2278
+ state.customTheme.colors ?? {},
2256
2279
  state.schemeModifierConfig.darkSuffix ?? "-dark",
2257
2280
  state.schemeModifierConfig.lightSuffix ?? "-light"
2258
2281
  );
@@ -2266,7 +2289,7 @@ function processStringOrExpressionHelper(node, state, parseClassName2, generateS
2266
2289
  const styleElements = [];
2267
2290
  if (baseClasses.length > 0) {
2268
2291
  const baseClassName = baseClasses.join(" ");
2269
- const styleObject = parseClassName2(baseClassName, state.customColors);
2292
+ const styleObject = parseClassName2(baseClassName, state.customTheme);
2270
2293
  const styleKey = generateStyleKey2(baseClassName);
2271
2294
  state.styleRegistry.set(styleKey, styleObject);
2272
2295
  styleElements.push(t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey)));
@@ -2373,7 +2396,7 @@ function processStaticClassNameWithModifiers(className, state, parseClassName2,
2373
2396
  let baseStyleExpression = null;
2374
2397
  if (baseClasses.length > 0) {
2375
2398
  const baseClassName = baseClasses.join(" ");
2376
- const baseStyleObject = parseClassName2(baseClassName, state.customColors);
2399
+ const baseStyleObject = parseClassName2(baseClassName, state.customTheme);
2377
2400
  const baseStyleKey = generateStyleKey2(baseClassName);
2378
2401
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
2379
2402
  baseStyleExpression = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey));
@@ -2394,7 +2417,7 @@ function processStaticClassNameWithModifiers(className, state, parseClassName2,
2394
2417
  }
2395
2418
  for (const [modifierType, modifiers] of modifiersByType) {
2396
2419
  const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
2397
- const modifierStyleObject = parseClassName2(modifierClassNames, state.customColors);
2420
+ const modifierStyleObject = parseClassName2(modifierClassNames, state.customTheme);
2398
2421
  const modifierStyleKey = generateStyleKey2(`${modifierType}_${modifierClassNames}`);
2399
2422
  state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
2400
2423
  const stateProperty = getStatePropertyForModifier(modifierType);
@@ -2443,7 +2466,7 @@ function processPlatformModifiers(platformModifiers, state, parseClassName2, gen
2443
2466
  const selectProperties = [];
2444
2467
  for (const [platform, modifiers] of modifiersByPlatform) {
2445
2468
  const classNames = modifiers.map((m) => m.baseClass).join(" ");
2446
- const styleObject = parseClassName2(classNames, state.customColors);
2469
+ const styleObject = parseClassName2(classNames, state.customTheme);
2447
2470
  const styleKey = generateStyleKey2(`${platform}_${classNames}`);
2448
2471
  state.styleRegistry.set(styleKey, styleObject);
2449
2472
  const styleReference = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
@@ -2708,7 +2731,7 @@ function processTwCall(className, path2, state, parseClassName2, generateStyleKe
2708
2731
  if (isSchemeModifier(modifier.modifier)) {
2709
2732
  const expanded = expandSchemeModifier(
2710
2733
  modifier,
2711
- state.customColors,
2734
+ state.customTheme.colors ?? {},
2712
2735
  state.schemeModifierConfig.darkSuffix ?? "-dark",
2713
2736
  state.schemeModifierConfig.lightSuffix ?? "-light"
2714
2737
  );
@@ -2720,7 +2743,7 @@ function processTwCall(className, path2, state, parseClassName2, generateStyleKe
2720
2743
  const objectProperties = [];
2721
2744
  if (baseClasses.length > 0) {
2722
2745
  const baseClassName = baseClasses.join(" ");
2723
- const baseStyleObject = parseClassName2(baseClassName, state.customColors);
2746
+ const baseStyleObject = parseClassName2(baseClassName, state.customTheme);
2724
2747
  const baseStyleKey = generateStyleKey2(baseClassName);
2725
2748
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
2726
2749
  objectProperties.push(
@@ -2744,7 +2767,7 @@ function processTwCall(className, path2, state, parseClassName2, generateStyleKe
2744
2767
  }
2745
2768
  for (const [modifierType, modifiers] of modifiersByType) {
2746
2769
  const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
2747
- const modifierStyleObject = parseClassName2(modifierClassNames, state.customColors);
2770
+ const modifierStyleObject = parseClassName2(modifierClassNames, state.customTheme);
2748
2771
  const modifierStyleKey = generateStyleKey2(`${modifierType}_${modifierClassNames}`);
2749
2772
  state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
2750
2773
  const propertyName = `${modifierType}Style`;
@@ -2848,7 +2871,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2848
2871
  state.twImportNames = /* @__PURE__ */ new Set();
2849
2872
  state.hasTwImport = false;
2850
2873
  state.functionComponentsNeedingColorScheme = /* @__PURE__ */ new Set();
2851
- state.customColors = extractCustomColors(state.file.opts.filename ?? "");
2874
+ state.customTheme = extractCustomTheme(state.file.opts.filename ?? "");
2852
2875
  state.schemeModifierConfig = schemeModifierConfig;
2853
2876
  },
2854
2877
  exit(path2, state) {
@@ -3014,7 +3037,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
3014
3037
  if (isSchemeModifier(modifier.modifier)) {
3015
3038
  const expanded = expandSchemeModifier(
3016
3039
  modifier,
3017
- state.customColors,
3040
+ state.customTheme.colors ?? {},
3018
3041
  state.schemeModifierConfig.darkSuffix,
3019
3042
  state.schemeModifierConfig.lightSuffix
3020
3043
  );
@@ -3034,7 +3057,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
3034
3057
  const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
3035
3058
  if (componentSupport?.supportedModifiers.includes("placeholder")) {
3036
3059
  const placeholderClasses = placeholderModifiers.map((m) => m.baseClass).join(" ");
3037
- const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customColors);
3060
+ const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customTheme.colors);
3038
3061
  if (placeholderColor) {
3039
3062
  addOrMergePlaceholderTextColorProp(jsxOpeningElement, placeholderColor, t);
3040
3063
  }
@@ -3070,7 +3093,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
3070
3093
  const styleArrayElements = [];
3071
3094
  if (hasBaseClasses) {
3072
3095
  const baseClassName = baseClasses.join(" ");
3073
- const baseStyleObject = parseClassName(baseClassName, state.customColors);
3096
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
3074
3097
  const baseStyleKey = generateStyleKey(baseClassName);
3075
3098
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
3076
3099
  styleArrayElements.push(
@@ -3110,7 +3133,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
3110
3133
  continue;
3111
3134
  }
3112
3135
  const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
3113
- const modifierStyleObject = parseClassName(modifierClassNames, state.customColors);
3136
+ const modifierStyleObject = parseClassName(modifierClassNames, state.customTheme);
3114
3137
  const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
3115
3138
  state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
3116
3139
  const stateProperty = getStatePropertyForModifier(modifierType);
@@ -3140,7 +3163,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
3140
3163
  const styleExpressions = [];
3141
3164
  if (hasBaseClasses) {
3142
3165
  const baseClassName = baseClasses.join(" ");
3143
- const baseStyleObject = parseClassName(baseClassName, state.customColors);
3166
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
3144
3167
  const baseStyleKey = generateStyleKey(baseClassName);
3145
3168
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
3146
3169
  styleExpressions.push(
@@ -3256,7 +3279,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
3256
3279
  path2.remove();
3257
3280
  return;
3258
3281
  }
3259
- const styleObject = parseClassName(classNameForStyle, state.customColors);
3282
+ const styleObject = parseClassName(classNameForStyle, state.customTheme);
3260
3283
  const styleKey = generateStyleKey(classNameForStyle);
3261
3284
  state.styleRegistry.set(styleKey, styleObject);
3262
3285
  const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
@@ -5,6 +5,7 @@
5
5
  import type { NodePath, PluginObj, PluginPass } from "@babel/core";
6
6
  import * as BabelTypes from "@babel/types";
7
7
  import type { StyleObject } from "../types/core.js";
8
+ import type { CustomTheme } from "./config-loader.js";
8
9
  import type { SchemeModifierConfig } from "../types/config.js";
9
10
  /**
10
11
  * Plugin options
@@ -50,7 +51,7 @@ type PluginState = PluginPass & {
50
51
  hasColorSchemeImport: boolean;
51
52
  needsColorSchemeImport: boolean;
52
53
  colorSchemeVariableName: string;
53
- customColors: Record<string, string>;
54
+ customTheme: CustomTheme;
54
55
  schemeModifierConfig: SchemeModifierConfig;
55
56
  supportedAttributes: Set<string>;
56
57
  attributePatterns: RegExp[];
@@ -18,7 +18,8 @@ import {
18
18
  } from "../parser/index.js";
19
19
  import type { StyleObject } from "../types/core.js";
20
20
  import { generateStyleKey } from "../utils/styleKey.js";
21
- import { extractCustomColors } from "./config-loader.js";
21
+ import type { CustomTheme } from "./config-loader.js";
22
+ import { extractCustomTheme } from "./config-loader.js";
22
23
 
23
24
  // Import utility functions
24
25
  import type { SchemeModifierConfig } from "../types/config.js";
@@ -99,7 +100,7 @@ type PluginState = PluginPass & {
99
100
  hasColorSchemeImport: boolean;
100
101
  needsColorSchemeImport: boolean;
101
102
  colorSchemeVariableName: string;
102
- customColors: Record<string, string>;
103
+ customTheme: CustomTheme;
103
104
  schemeModifierConfig: SchemeModifierConfig;
104
105
  supportedAttributes: Set<string>;
105
106
  attributePatterns: RegExp[];
@@ -231,8 +232,8 @@ export default function reactNativeTailwindBabelPlugin(
231
232
  state.hasTwImport = false;
232
233
  state.functionComponentsNeedingColorScheme = new Set();
233
234
 
234
- // Load custom colors from tailwind.config.*
235
- state.customColors = extractCustomColors(state.file.opts.filename ?? "");
235
+ // Load custom theme from tailwind.config.*
236
+ state.customTheme = extractCustomTheme(state.file.opts.filename ?? "");
236
237
 
237
238
  // Use scheme modifier config from plugin options
238
239
  state.schemeModifierConfig = schemeModifierConfig;
@@ -480,7 +481,7 @@ export default function reactNativeTailwindBabelPlugin(
480
481
  // Expand scheme: into dark: and light:
481
482
  const expanded = expandSchemeModifier(
482
483
  modifier,
483
- state.customColors,
484
+ state.customTheme.colors ?? {},
484
485
  state.schemeModifierConfig.darkSuffix,
485
486
  state.schemeModifierConfig.lightSuffix,
486
487
  );
@@ -507,7 +508,7 @@ export default function reactNativeTailwindBabelPlugin(
507
508
 
508
509
  if (componentSupport?.supportedModifiers.includes("placeholder")) {
509
510
  const placeholderClasses = placeholderModifiers.map((m) => m.baseClass).join(" ");
510
- const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customColors);
511
+ const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customTheme.colors);
511
512
 
512
513
  if (placeholderColor) {
513
514
  // Add or merge placeholderTextColor prop
@@ -561,7 +562,7 @@ export default function reactNativeTailwindBabelPlugin(
561
562
  // Add base classes
562
563
  if (hasBaseClasses) {
563
564
  const baseClassName = baseClasses.join(" ");
564
- const baseStyleObject = parseClassName(baseClassName, state.customColors);
565
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
565
566
  const baseStyleKey = generateStyleKey(baseClassName);
566
567
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
567
568
  styleArrayElements.push(
@@ -611,7 +612,7 @@ export default function reactNativeTailwindBabelPlugin(
611
612
  }
612
613
 
613
614
  const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
614
- const modifierStyleObject = parseClassName(modifierClassNames, state.customColors);
615
+ const modifierStyleObject = parseClassName(modifierClassNames, state.customTheme);
615
616
  const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
616
617
  state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
617
618
 
@@ -653,7 +654,7 @@ export default function reactNativeTailwindBabelPlugin(
653
654
  // Add base classes
654
655
  if (hasBaseClasses) {
655
656
  const baseClassName = baseClasses.join(" ");
656
- const baseStyleObject = parseClassName(baseClassName, state.customColors);
657
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
657
658
  const baseStyleKey = generateStyleKey(baseClassName);
658
659
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
659
660
  styleExpressions.push(
@@ -815,7 +816,7 @@ export default function reactNativeTailwindBabelPlugin(
815
816
  return;
816
817
  }
817
818
 
818
- const styleObject = parseClassName(classNameForStyle, state.customColors);
819
+ const styleObject = parseClassName(classNameForStyle, state.customTheme);
819
820
  const styleKey = generateStyleKey(classNameForStyle);
820
821
  state.styleRegistry.set(styleKey, styleObject);
821
822
 
@@ -2,14 +2,14 @@
2
2
  * Utility functions for processing color scheme modifiers (dark:, light:)
3
3
  */
4
4
  import type * as BabelTypes from "@babel/types";
5
- import type { ParsedModifier } from "../../parser/index.js";
5
+ import type { CustomTheme, ParsedModifier } from "../../parser/index.js";
6
6
  import type { StyleObject } from "../../types/core.js";
7
7
  /**
8
8
  * Plugin state interface (subset needed for color scheme modifier processing)
9
9
  */
10
10
  export interface ColorSchemeModifierProcessingState {
11
11
  styleRegistry: Map<string, StyleObject>;
12
- customColors: Record<string, string>;
12
+ customTheme: CustomTheme;
13
13
  stylesIdentifier: string;
14
14
  needsColorSchemeImport: boolean;
15
15
  colorSchemeVariableName: string;
@@ -31,4 +31,4 @@ export interface ColorSchemeModifierProcessingState {
31
31
  * _twColorScheme === 'light' && styles._light_bg_white
32
32
  * ]
33
33
  */
34
- export declare function processColorSchemeModifiers(colorSchemeModifiers: ParsedModifier[], state: ColorSchemeModifierProcessingState, parseClassName: (className: string, customColors: Record<string, string>) => StyleObject, generateStyleKey: (className: string) => string, t: typeof BabelTypes): BabelTypes.Expression[];
34
+ export declare function processColorSchemeModifiers(colorSchemeModifiers: ParsedModifier[], state: ColorSchemeModifierProcessingState, parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject, generateStyleKey: (className: string) => string, t: typeof BabelTypes): BabelTypes.Expression[];