diff --git a/packages/ui/.gitignore b/packages/ui/.gitignore
new file mode 100644
index 0000000..2068747
--- /dev/null
+++ b/packages/ui/.gitignore
@@ -0,0 +1 @@
+storybook-static
diff --git a/packages/ui/.storybook/main.ts b/packages/ui/.storybook/main.ts
new file mode 100644
index 0000000..5de8877
--- /dev/null
+++ b/packages/ui/.storybook/main.ts
@@ -0,0 +1,75 @@
+import type { StorybookConfig } from "@storybook/react-webpack5";
+
+import { dirname, join, resolve } from "node:path";
+
+// ? for monorepo support
+function getAbsolutePath(value: string): string {
+ return dirname(require.resolve(join(value, "package.json")));
+}
+
+// ? https://github.com/storybookjs/addon-react-native-web/issues/45
+export default {
+ stories: ["../src/**/*.mdx", "../src/**/*.stories.@(ts|tsx)"],
+ addons: [
+ getAbsolutePath("@storybook/addon-webpack5-compiler-babel"),
+ getAbsolutePath("@storybook/addon-onboarding"),
+ getAbsolutePath("@storybook/addon-essentials"),
+ getAbsolutePath("@chromatic-com/storybook"),
+ getAbsolutePath("@storybook/addon-interactions"),
+ {
+ name: getAbsolutePath("@storybook/addon-react-native-web"),
+ options: {
+ modulesToTranspile: [
+ "react-native-reanimated",
+ "nativewind",
+ "react-native-css-interop",
+ ],
+ babelPresets: ["nativewind/babel"],
+ babelPresetReactOptions: { jsxImportSource: "nativewind" },
+ babelPlugins: [
+ "react-native-reanimated/plugin",
+ [
+ "@babel/plugin-transform-react-jsx",
+ {
+ runtime: "automatic",
+ importSource: "nativewind",
+ },
+ ],
+ ],
+ },
+ },
+ ],
+ framework: {
+ name: getAbsolutePath("@storybook/react-webpack5"),
+ options: { fastRefresh: true },
+ },
+ typescript: {
+ check: false,
+ checkOptions: {},
+ },
+ docs: {
+ autodocs: "tag",
+ },
+ webpackFinal: (config) => {
+ if (config.module?.rules) {
+ config.module.rules.push({
+ test: /\.css$/,
+ use: [
+ {
+ loader: "postcss-loader",
+ options: {
+ postcssOptions: {
+ plugins: [require("tailwindcss"), require("autoprefixer")],
+ },
+ },
+ },
+ ],
+ include: resolve(__dirname, "../"),
+ });
+ }
+
+ return {
+ ...config,
+ };
+ },
+} satisfies StorybookConfig;
diff --git a/packages/ui/.storybook/preview.ts b/packages/ui/.storybook/preview.ts
new file mode 100644
index 0000000..e5f99ad
--- /dev/null
+++ b/packages/ui/.storybook/preview.ts
@@ -0,0 +1,16 @@
+import type { Preview } from "@storybook/react";
+
+import "../global.css";
+
+const preview: Preview = {
+ parameters: {
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+ },
+};
+
+export default preview;
diff --git a/packages/ui/global.css b/packages/ui/global.css
new file mode 100644
index 0000000..b5c61c9
--- /dev/null
+++ b/packages/ui/global.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/packages/ui/nativewind-env.d.ts b/packages/ui/nativewind-env.d.ts
new file mode 100644
index 0000000..a13e313
--- /dev/null
+++ b/packages/ui/nativewind-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 2cd9636..974e1a6 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -9,14 +9,19 @@
},
"./*": {
"types": "./dist/src/*.d.ts",
- "default": ["./src/*.ts", "./src/*.tsx"]
+ "default": [
+ "./src/*.ts",
+ "./src/*.tsx"
+ ]
}
},
"scripts": {
"build": "tsc",
+ "build-storybook": "storybook build",
"clean": "git clean -xdf .cache .turbo dist node_modules",
"dev": "tsc",
"lint": "biome lint --fix --unsafe --no-errors-on-unmatched",
+ "storybook": "storybook dev -p 6006",
"type-check": "tsc --noEmit"
},
"dependencies": {
@@ -25,8 +30,24 @@
"tailwindcss": "catalog:"
},
"devDependencies": {
+ "@babel/plugin-transform-react-jsx": "^7.25.9",
"@biomejs/biome": "catalog:",
+ "@chromatic-com/storybook": "^3.2.2",
"@infinite-loop-factory/config-typescript": "workspace:^",
+ "@storybook/addon-essentials": "^8.4.1",
+ "@storybook/addon-interactions": "^8.4.1",
+ "@storybook/addon-onboarding": "^8.4.1",
+ "@storybook/addon-react-native-web": "^0.0.26",
+ "@storybook/addon-webpack5-compiler-babel": "^3.0.3",
+ "@storybook/blocks": "^8.4.1",
+ "@storybook/react": "^8.4.1",
+ "@storybook/react-webpack5": "^8.4.1",
+ "@storybook/test": "^8.4.1",
+ "autoprefixer": "^10.4.20",
+ "postcss-loader": "^8.1.1",
+ "react-native-css-interop": "^0.1.20",
+ "react-native-reanimated": "^3.16.1",
+ "storybook": "^8.4.1",
"type-fest": "catalog:",
"typescript": "catalog:"
}
diff --git a/packages/ui/src/button/button.stories.tsx b/packages/ui/src/button/button.stories.tsx
new file mode 100644
index 0000000..d382133
--- /dev/null
+++ b/packages/ui/src/button/button.stories.tsx
@@ -0,0 +1,34 @@
+import type { Meta, StoryObj } from "@storybook/react";
+
+import { View } from "react-native";
+import MyButton from "./button";
+
+const meta = {
+ title: "MyButton",
+ component: MyButton,
+ argTypes: {
+ onPress: { action: "pressed the button" },
+ },
+ args: {
+ title: "Hello world",
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Basic: Story = {};
+
+export const AnotherExample: Story = {
+ args: {
+ title: "Another example",
+ },
+};
diff --git a/packages/ui/src/button/button.tsx b/packages/ui/src/button/button.tsx
new file mode 100644
index 0000000..dd7d6b6
--- /dev/null
+++ b/packages/ui/src/button/button.tsx
@@ -0,0 +1,15 @@
+import { Text, TouchableOpacity } from "react-native";
+
+export default function Button({
+ title,
+ onPress,
+}: { title: string; onPress?: () => void }) {
+ return (
+
+ {title}
+
+ );
+}
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
deleted file mode 100644
index 3b39966..0000000
--- a/packages/ui/src/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export function add(a: number, b: number) {
- return a + b;
-}
diff --git a/packages/ui/tailwind.config.ts b/packages/ui/tailwind.config.ts
new file mode 100644
index 0000000..43ac3ee
--- /dev/null
+++ b/packages/ui/tailwind.config.ts
@@ -0,0 +1,10 @@
+import type { Config } from "tailwindcss";
+
+// @ts-expect-error - no types
+import nativewind from "nativewind/preset";
+
+export default {
+ content: ["src/**/*.{js,jsx,ts,tsx}"],
+ presets: [nativewind],
+ plugins: [],
+} satisfies Config;
diff --git a/packages/ui/tsconfig.build.json b/packages/ui/tsconfig.build.json
new file mode 100644
index 0000000..0f59732
--- /dev/null
+++ b/packages/ui/tsconfig.build.json
@@ -0,0 +1,4 @@
+{
+ "extends": "./tsconfig.json",
+ "exclude": ["**/*.stories.tsx", "node_modules"]
+}
diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json
index e37cda1..7215978 100644
--- a/packages/ui/tsconfig.json
+++ b/packages/ui/tsconfig.json
@@ -4,6 +4,6 @@
"jsx": "preserve",
"lib": ["ES2022", "dom", "dom.iterable"]
},
- "include": ["src"],
+ "include": ["src", "nativewind-env.d.ts"],
"exclude": ["node_modules"]
}