react-native-web-tailwind-compat 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexander Nicholson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # react-native-web-tailwind-compat
2
+ Tailwind 4 uses CSS layers to organise styles, but react-native-web does not yet support CSS layers, resulting in react-native-web styles overriding tailwind styles. This package fixes this by wrapping react-native-web reset styles in a `@layer rnw {}` block.
3
+
4
+ ## Installation
5
+
6
+ ```bash
7
+ npm install react-native-web-tailwind-compat
8
+ ```
9
+
10
+ Import the package at the top of your js entry point:
11
+
12
+ ```ts
13
+ // src/index.tsx
14
+ import 'react-native-web-tailwind-compat';
15
+ // Other imports...
16
+ ```
17
+
18
+ Define the layer *before* importing tailwind styles:
19
+
20
+ ```css
21
+ /* src/global.css */
22
+ @layer rnw;
23
+ @import 'tailwindcss';
24
+ ```
25
+
26
+ ## Server rendering
27
+ If you want to include react-native-web styles in SSR, use the provided `getServerStyleSheet` function, for example with Next.js:
28
+
29
+ ```tsx
30
+ //ReactNativeWebStyleSheet.tsx
31
+ "use client";
32
+ import { useRef } from "react";
33
+ import { useServerInsertedHTML } from "next/navigation";
34
+ import { getServerStylesheet } from "react-native-web-tailwind-compat";
35
+
36
+ export function ReactNativeWebLayeredStyleSheet() {
37
+ const hasInserted = useRef(false);
38
+ useServerInsertedHTML(() => {
39
+ if (hasInserted.current) return;
40
+ hasInserted.current = true;
41
+
42
+ const sheet = getServerStylesheet();
43
+
44
+ return (
45
+ <style
46
+ id={sheet.id}
47
+ dangerouslySetInnerHTML={{
48
+ __html: sheet.textContent,
49
+ }}
50
+
51
+ />
52
+ );
53
+ });
54
+ return null;
55
+ }
56
+
57
+ ```
@@ -0,0 +1,41 @@
1
+ const require_utils = require('./utils.cjs');
2
+
3
+ //#region src/ReactNativeWebTailwindCompat.ts
4
+ if (typeof window !== "undefined") {
5
+ let elemProxy;
6
+ const _getElementById = document.getElementById;
7
+ document.getElementById = function(id) {
8
+ return id !== "react-native-stylesheet" ? _getElementById.call(document, id) : elemProxy ??= { sheet: buildRNWProxy() };
9
+ };
10
+ function buildRNWProxy() {
11
+ const flattenedSheet = new CSSStyleSheet();
12
+ let layeredSheet = document.getElementById(require_utils.NEW_SHEET_ID)?.sheet;
13
+ if (!layeredSheet) {
14
+ const styleElem = document.createElement("style");
15
+ styleElem.id = require_utils.NEW_SHEET_ID;
16
+ document.head.prepend(styleElem);
17
+ layeredSheet = styleElem.sheet;
18
+ }
19
+ if (!layeredSheet) return flattenedSheet;
20
+ if (!(layeredSheet.cssRules[0] instanceof CSSLayerBlockRule)) layeredSheet.insertRule("@layer rnw {}", 0);
21
+ flattenRules(layeredSheet.cssRules, flattenedSheet);
22
+ return new Proxy(flattenedSheet, { get(target, prop) {
23
+ if (prop === "insertRule") return function insertRule(text, index) {
24
+ flattenedSheet.insertRule(text, index);
25
+ const cutoffIndex = [...flattenedSheet.cssRules].findIndex((rule) => require_utils.layerBoundaryRegex.exec(rule.cssText));
26
+ if (cutoffIndex === -1 || index <= cutoffIndex) {
27
+ const layerRule = layeredSheet.cssRules[0];
28
+ layerRule.insertRule(text, layerRule.cssRules.length);
29
+ } else layeredSheet.insertRule(text, layeredSheet.cssRules.length);
30
+ };
31
+ const value = target[prop];
32
+ return typeof value === "function" ? value.bind(target) : value;
33
+ } });
34
+ }
35
+ function flattenRules(rules, targetSheet) {
36
+ for (const rule of rules) if (rule instanceof CSSLayerBlockRule) flattenRules(rule.cssRules, targetSheet);
37
+ else targetSheet.insertRule(rule.cssText, targetSheet.cssRules.length);
38
+ }
39
+ }
40
+
41
+ //#endregion
@@ -0,0 +1,43 @@
1
+ import { NEW_SHEET_ID, layerBoundaryRegex } from "./utils.mjs";
2
+
3
+ //#region src/ReactNativeWebTailwindCompat.ts
4
+ if (typeof window !== "undefined") {
5
+ let elemProxy;
6
+ const _getElementById = document.getElementById;
7
+ document.getElementById = function(id) {
8
+ return id !== "react-native-stylesheet" ? _getElementById.call(document, id) : elemProxy ??= { sheet: buildRNWProxy() };
9
+ };
10
+ function buildRNWProxy() {
11
+ const flattenedSheet = new CSSStyleSheet();
12
+ let layeredSheet = document.getElementById(NEW_SHEET_ID)?.sheet;
13
+ if (!layeredSheet) {
14
+ const styleElem = document.createElement("style");
15
+ styleElem.id = NEW_SHEET_ID;
16
+ document.head.prepend(styleElem);
17
+ layeredSheet = styleElem.sheet;
18
+ }
19
+ if (!layeredSheet) return flattenedSheet;
20
+ if (!(layeredSheet.cssRules[0] instanceof CSSLayerBlockRule)) layeredSheet.insertRule("@layer rnw {}", 0);
21
+ flattenRules(layeredSheet.cssRules, flattenedSheet);
22
+ return new Proxy(flattenedSheet, { get(target, prop) {
23
+ if (prop === "insertRule") return function insertRule(text, index) {
24
+ flattenedSheet.insertRule(text, index);
25
+ const cutoffIndex = [...flattenedSheet.cssRules].findIndex((rule) => layerBoundaryRegex.exec(rule.cssText));
26
+ if (cutoffIndex === -1 || index <= cutoffIndex) {
27
+ const layerRule = layeredSheet.cssRules[0];
28
+ layerRule.insertRule(text, layerRule.cssRules.length);
29
+ } else layeredSheet.insertRule(text, layeredSheet.cssRules.length);
30
+ };
31
+ const value = target[prop];
32
+ return typeof value === "function" ? value.bind(target) : value;
33
+ } });
34
+ }
35
+ function flattenRules(rules, targetSheet) {
36
+ for (const rule of rules) if (rule instanceof CSSLayerBlockRule) flattenRules(rule.cssRules, targetSheet);
37
+ else targetSheet.insertRule(rule.cssText, targetSheet.cssRules.length);
38
+ }
39
+ }
40
+
41
+ //#endregion
42
+ export { };
43
+ //# sourceMappingURL=ReactNativeWebTailwindCompat.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactNativeWebTailwindCompat.mjs","names":["elemProxy: HTMLStyleElement"],"sources":["../src/ReactNativeWebTailwindCompat.ts"],"sourcesContent":["import { layerBoundaryRegex, NEW_SHEET_ID } from \"./utils\";\n\nif (typeof window !== \"undefined\") {\n let elemProxy: HTMLStyleElement;\n const _getElementById = document.getElementById;\n document.getElementById = function (id: string) {\n return id !== \"react-native-stylesheet\"\n ? _getElementById.call(document, id)\n : (elemProxy ??= { sheet: buildRNWProxy() } as HTMLStyleElement);\n };\n\n function buildRNWProxy() {\n const flattenedSheet = new CSSStyleSheet();\n let layeredSheet = (\n document.getElementById(NEW_SHEET_ID) as HTMLStyleElement\n )?.sheet;\n\n if (!layeredSheet) {\n const styleElem = document.createElement(\"style\");\n styleElem.id = NEW_SHEET_ID;\n document.head.prepend(styleElem);\n layeredSheet = styleElem.sheet;\n }\n if (!layeredSheet) return flattenedSheet;\n // ensure that the first rule in the layered sheet is a layer\n if (!(layeredSheet.cssRules[0] instanceof CSSLayerBlockRule)) {\n layeredSheet.insertRule(\"@layer rnw {}\", 0);\n }\n // Traverse the layered sheet to build the flattened sheet\n flattenRules(layeredSheet.cssRules, flattenedSheet);\n\n return new Proxy(flattenedSheet, {\n get(target, prop) {\n if (prop === \"insertRule\") {\n return function insertRule(text: string, index: number) {\n flattenedSheet.insertRule(text, index);\n // find the index of the groups\n const cutoffIndex = [...flattenedSheet.cssRules].findIndex((rule) =>\n layerBoundaryRegex.exec(rule.cssText),\n );\n if (cutoffIndex === -1 || index <= cutoffIndex) {\n // insert into the layer\n const layerRule = layeredSheet.cssRules[0] as CSSLayerBlockRule;\n layerRule.insertRule(text, layerRule.cssRules.length);\n } else {\n // insert into the sheet normally\n layeredSheet.insertRule(text, layeredSheet.cssRules.length);\n }\n };\n }\n const value = (target as any)[prop];\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n }\n\n function flattenRules(\n rules: CSSRuleList | CSSRule[],\n targetSheet: CSSStyleSheet,\n ) {\n for (const rule of rules) {\n if (rule instanceof CSSLayerBlockRule) {\n flattenRules(rule.cssRules, targetSheet);\n } else {\n targetSheet.insertRule(rule.cssText, targetSheet.cssRules.length);\n }\n }\n }\n}\n"],"mappings":";;;AAEA,IAAI,OAAO,WAAW,aAAa;CACjC,IAAIA;CACJ,MAAM,kBAAkB,SAAS;AACjC,UAAS,iBAAiB,SAAU,IAAY;AAC9C,SAAO,OAAO,4BACV,gBAAgB,KAAK,UAAU,GAAG,GACjC,cAAc,EAAE,OAAO,eAAe,EAAE;;CAG/C,SAAS,gBAAgB;EACvB,MAAM,iBAAiB,IAAI,eAAe;EAC1C,IAAI,eACF,SAAS,eAAe,aAAa,EACpC;AAEH,MAAI,CAAC,cAAc;GACjB,MAAM,YAAY,SAAS,cAAc,QAAQ;AACjD,aAAU,KAAK;AACf,YAAS,KAAK,QAAQ,UAAU;AAChC,kBAAe,UAAU;;AAE3B,MAAI,CAAC,aAAc,QAAO;AAE1B,MAAI,EAAE,aAAa,SAAS,cAAc,mBACxC,cAAa,WAAW,iBAAiB,EAAE;AAG7C,eAAa,aAAa,UAAU,eAAe;AAEnD,SAAO,IAAI,MAAM,gBAAgB,EAC/B,IAAI,QAAQ,MAAM;AAChB,OAAI,SAAS,aACX,QAAO,SAAS,WAAW,MAAc,OAAe;AACtD,mBAAe,WAAW,MAAM,MAAM;IAEtC,MAAM,cAAc,CAAC,GAAG,eAAe,SAAS,CAAC,WAAW,SAC1D,mBAAmB,KAAK,KAAK,QAAQ,CACtC;AACD,QAAI,gBAAgB,MAAM,SAAS,aAAa;KAE9C,MAAM,YAAY,aAAa,SAAS;AACxC,eAAU,WAAW,MAAM,UAAU,SAAS,OAAO;UAGrD,cAAa,WAAW,MAAM,aAAa,SAAS,OAAO;;GAIjE,MAAM,QAAS,OAAe;AAC9B,UAAO,OAAO,UAAU,aAAa,MAAM,KAAK,OAAO,GAAG;KAE7D,CAAC;;CAGJ,SAAS,aACP,OACA,aACA;AACA,OAAK,MAAM,QAAQ,MACjB,KAAI,gBAAgB,kBAClB,cAAa,KAAK,UAAU,YAAY;MAExC,aAAY,WAAW,KAAK,SAAS,YAAY,SAAS,OAAO"}
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ const require_utils = require('./utils.cjs');
4
+ let react_native = require("react-native");
5
+
6
+ //#region src/getServerStyleSheet.ts
7
+ function getServerStyleSheet() {
8
+ return {
9
+ id: require_utils.NEW_SHEET_ID,
10
+ textContent: processSheetText(react_native.StyleSheet.getSheet().textContent)
11
+ };
12
+ }
13
+ function processSheetText(text) {
14
+ const endIndex = require_utils.layerBoundaryRegex.exec(text)?.index ?? 0;
15
+ return `@layer rnw {\n${text.substring(0, endIndex)}}\n${text.substring(endIndex)}`;
16
+ }
17
+
18
+ //#endregion
19
+ exports.getServerStyleSheet = getServerStyleSheet;
@@ -0,0 +1,8 @@
1
+ //#region src/getServerStyleSheet.d.ts
2
+ declare function getServerStyleSheet(): {
3
+ id: string;
4
+ textContent: string;
5
+ };
6
+ //#endregion
7
+ export { getServerStyleSheet };
8
+ //# sourceMappingURL=getServerStyleSheet.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getServerStyleSheet.d.cts","names":[],"sources":["../src/getServerStyleSheet.ts"],"sourcesContent":[],"mappings":";iBAIgB,mBAAA,CAAA;EAAA,EAAA,EAAA,MAAA"}
@@ -0,0 +1,8 @@
1
+ //#region src/getServerStyleSheet.d.ts
2
+ declare function getServerStyleSheet(): {
3
+ id: string;
4
+ textContent: string;
5
+ };
6
+ //#endregion
7
+ export { getServerStyleSheet };
8
+ //# sourceMappingURL=getServerStyleSheet.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getServerStyleSheet.d.mts","names":[],"sources":["../src/getServerStyleSheet.ts"],"sourcesContent":[],"mappings":";iBAIgB,mBAAA,CAAA;EAAA,EAAA,EAAA,MAAA"}
@@ -0,0 +1,20 @@
1
+ "use client";
2
+
3
+ import { NEW_SHEET_ID, layerBoundaryRegex } from "./utils.mjs";
4
+ import { StyleSheet } from "react-native";
5
+
6
+ //#region src/getServerStyleSheet.ts
7
+ function getServerStyleSheet() {
8
+ return {
9
+ id: NEW_SHEET_ID,
10
+ textContent: processSheetText(StyleSheet.getSheet().textContent)
11
+ };
12
+ }
13
+ function processSheetText(text) {
14
+ const endIndex = layerBoundaryRegex.exec(text)?.index ?? 0;
15
+ return `@layer rnw {\n${text.substring(0, endIndex)}}\n${text.substring(endIndex)}`;
16
+ }
17
+
18
+ //#endregion
19
+ export { getServerStyleSheet };
20
+ //# sourceMappingURL=getServerStyleSheet.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getServerStyleSheet.mjs","names":[],"sources":["../src/getServerStyleSheet.ts"],"sourcesContent":["\"use client\";\nimport { StyleSheet } from \"react-native\";\nimport { layerBoundaryRegex, NEW_SHEET_ID } from \"./utils\";\n\nexport function getServerStyleSheet() {\n const sheet = (StyleSheet as any).getSheet();\n\n return {\n id: NEW_SHEET_ID,\n textContent: processSheetText(sheet.textContent),\n };\n}\n\nexport function processSheetText(text: string): string {\n const endIndex = layerBoundaryRegex.exec(text)?.index ?? 0;\n return `@layer rnw {\\n${text.substring(0, endIndex)}}\\n${text.substring(endIndex)}`;\n}\n"],"mappings":";;;;;;AAIA,SAAgB,sBAAsB;AAGpC,QAAO;EACL,IAAI;EACJ,aAAa,iBAJA,WAAmB,UAAU,CAIN,YAAY;EACjD;;AAGH,SAAgB,iBAAiB,MAAsB;CACrD,MAAM,WAAW,mBAAmB,KAAK,KAAK,EAAE,SAAS;AACzD,QAAO,iBAAiB,KAAK,UAAU,GAAG,SAAS,CAAC,KAAK,KAAK,UAAU,SAAS"}
package/dist/index.cjs ADDED
@@ -0,0 +1,4 @@
1
+ require('./ReactNativeWebTailwindCompat.cjs');
2
+ const require_getServerStyleSheet = require('./getServerStyleSheet.cjs');
3
+
4
+ exports.getServerStyleSheet = require_getServerStyleSheet.getServerStyleSheet;
@@ -0,0 +1,2 @@
1
+ import { getServerStyleSheet } from "./getServerStyleSheet.cjs";
2
+ export { getServerStyleSheet };
@@ -0,0 +1,2 @@
1
+ import { getServerStyleSheet } from "./getServerStyleSheet.mjs";
2
+ export { getServerStyleSheet };
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import "./ReactNativeWebTailwindCompat.mjs";
2
+ import { getServerStyleSheet } from "./getServerStyleSheet.mjs";
3
+
4
+ export { getServerStyleSheet };
package/dist/utils.cjs ADDED
@@ -0,0 +1,8 @@
1
+
2
+ //#region src/utils.ts
3
+ const NEW_SHEET_ID = "react-native-stylesheet-layered";
4
+ const layerBoundaryRegex = /\[stylesheet-group="[^01]"]/;
5
+
6
+ //#endregion
7
+ exports.NEW_SHEET_ID = NEW_SHEET_ID;
8
+ exports.layerBoundaryRegex = layerBoundaryRegex;
package/dist/utils.mjs ADDED
@@ -0,0 +1,7 @@
1
+ //#region src/utils.ts
2
+ const NEW_SHEET_ID = "react-native-stylesheet-layered";
3
+ const layerBoundaryRegex = /\[stylesheet-group="[^01]"]/;
4
+
5
+ //#endregion
6
+ export { NEW_SHEET_ID, layerBoundaryRegex };
7
+ //# sourceMappingURL=utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["export const NEW_SHEET_ID = \"react-native-stylesheet-layered\";\n\nexport const layerBoundaryRegex = /\\[stylesheet-group=\"[^01]\"]/;\n"],"mappings":";AAAA,MAAa,eAAe;AAE5B,MAAa,qBAAqB"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "react-native-web-tailwind-compat",
3
+ "version": "1.0.0",
4
+ "author": "Alexander Nicholson",
5
+ "description": "Adds CSS layers to react native web styles, allowing it to be used with TailwindCSS v4",
6
+ "homepage": "https://github.com/a16n-dev/uniwind-plugin-next#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/a16n-dev/uniwind-plugin-next.git"
10
+ },
11
+ "devDependencies": {
12
+ "@vitest/ui": "^4.0.16",
13
+ "tsdown": "^0.18.2",
14
+ "vitest": "^4.0.16"
15
+ },
16
+ "peerDependencies": {
17
+ "react-native": "^0.79",
18
+ "react-native-web": "^0.19"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "require": "./dist/index.cjs",
23
+ "import": "./dist/index.mjs"
24
+ },
25
+ "./package.json": "./package.json"
26
+ },
27
+ "main": "./dist/index.cjs",
28
+ "module": "./dist/index.mjs",
29
+ "types": "./dist/index.d.cts",
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsdown",
36
+ "dev": "tsdown --watch",
37
+ "typecheck": "tsc --noEmit",
38
+ "test": "vitest run"
39
+ }
40
+ }