diff --git a/package-lock.json b/package-lock.json index ea79136..ec90390 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,11 @@ "name": "brew", "version": "0.0.0", "dependencies": { + "@tailwindcss/vite": "^4.3.0", "lucide-react": "^1.17.0", "react": "^19.2.6", - "react-dom": "^19.2.6" + "react-dom": "^19.2.6", + "tailwindcss": "^4.3.0" }, "devDependencies": { "@eslint/js": "^10.0.1", @@ -268,7 +270,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -280,7 +281,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -291,7 +291,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -496,7 +495,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -507,7 +505,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -518,7 +515,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -528,14 +524,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -546,7 +540,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -565,7 +558,6 @@ "version": "0.133.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -578,7 +570,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -595,7 +586,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -612,7 +602,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -629,7 +618,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -646,7 +634,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -663,7 +650,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "glibc" ], @@ -683,7 +669,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "musl" ], @@ -703,7 +688,6 @@ "cpu": [ "ppc64" ], - "dev": true, "libc": [ "glibc" ], @@ -723,7 +707,6 @@ "cpu": [ "s390x" ], - "dev": true, "libc": [ "glibc" ], @@ -743,7 +726,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "glibc" ], @@ -763,7 +745,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "musl" ], @@ -783,7 +764,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -800,7 +780,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -819,7 +798,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -836,7 +814,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -850,14 +827,281 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, "license": "MIT" }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "tailwindcss": "4.3.0" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1120,7 +1364,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -1133,6 +1376,19 @@ "dev": true, "license": "ISC" }, + "node_modules/enhanced-resolve": { + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz", + "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1363,7 +1619,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1432,7 +1687,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1479,6 +1733,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -1546,6 +1806,15 @@ "dev": true, "license": "ISC" }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1628,7 +1897,6 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -1661,7 +1929,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1682,7 +1949,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1703,7 +1969,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1724,7 +1989,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1745,7 +2009,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1766,7 +2029,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "glibc" ], @@ -1790,7 +2052,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "musl" ], @@ -1814,7 +2075,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "glibc" ], @@ -1838,7 +2098,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "musl" ], @@ -1862,7 +2121,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1883,7 +2141,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1932,6 +2189,15 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -1959,7 +2225,6 @@ "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, "funding": [ { "type": "github", @@ -2065,14 +2330,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2085,7 +2348,6 @@ "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2155,7 +2417,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", - "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.133.0", @@ -2228,17 +2489,34 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2255,7 +2533,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true }, @@ -2317,7 +2594,6 @@ "version": "8.0.16", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", - "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", diff --git a/package.json b/package.json index 1dba9b7..d2a8e2b 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,11 @@ "preview": "vite preview" }, "dependencies": { + "@tailwindcss/vite": "^4.3.0", "lucide-react": "^1.17.0", "react": "^19.2.6", - "react-dom": "^19.2.6" + "react-dom": "^19.2.6", + "tailwindcss": "^4.3.0" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/src/App.jsx b/src/App.jsx index 7471a41..e1ddebf 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -3,16 +3,13 @@ import { AuthContext } from "./AuthContext"; import Login from "./Login"; import Register from "./Register"; -// ─── Storage helpers with Browser/localStorage Fallback ─── +// ─── Storage helpers ─── const STORAGE_KEY = "coffee-logbook-data"; const defaultData = { beans: [], brewLogs: [], lastSyncedAt: null }; const storage = { get: async (key) => { try { - if (window.storage && typeof window.storage.get === "function") { - return await window.storage.get(key); - } const val = localStorage.getItem(key); return val ? { value: val } : null; } catch { @@ -21,10 +18,6 @@ const storage = { }, set: async (key, val) => { try { - if (window.storage && typeof window.storage.set === "function") { - await window.storage.set(key, val); - return; - } localStorage.setItem(key, val); } catch (e) { console.error("Storage set failed:", e); @@ -65,441 +58,9 @@ const METHOD_LABELS = { pourover: "Pour Over", espresso: "Espresso", coldbrew: " const METHOD_ICONS = { pourover: "☕", espresso: "⚡", coldbrew: "❄️" }; const METHOD_COLORS = { pourover: "#8B6914", espresso: "#5C3317", coldbrew: "#2F4F6F" }; -// ─── Styles ─── -const styles = ` - @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400&family=Source+Sans+3:wght@300;400;500;600&display=swap'); - - * { box-sizing: border-box; margin: 0; padding: 0; } - - :root { - --bg: #FAF6F1; - --bg2: #F3EDE4; - --card: #FFFFFF; - --text: #2C1810; - --text2: #6B5744; - --text3: #9C8B7A; - --accent: #8B6914; - --accent2: #C4941A; - --border: #E8DFD3; - --espresso: #5C3317; - --coldbrew: #2F4F6F; - --pourover: #8B6914; - --danger: #B44040; - --shadow: 0 1px 3px rgba(44,24,16,0.06), 0 4px 12px rgba(44,24,16,0.04); - --shadow-lg: 0 4px 16px rgba(44,24,16,0.08), 0 12px 32px rgba(44,24,16,0.06); - --radius: 12px; - --radius-sm: 8px; - } - - .app { - font-family: 'Source Sans 3', sans-serif; - background: var(--bg); - color: var(--text); - min-height: 100vh; - max-width: 480px; - margin: 0 auto; - position: relative; - overflow-x: hidden; - } - - h1, h2, h3, .display { font-family: 'Playfair Display', serif; } - - /* ── Header ── */ - .header { - padding: 24px 20px 16px; - display: flex; - align-items: center; - justify-content: space-between; - position: sticky; - top: 0; - background: var(--bg); - z-index: 50; - border-bottom: 1px solid var(--border); - } - .header h1 { font-size: 22px; font-weight: 600; letter-spacing: -0.3px; } - .header-sub { font-size: 12px; color: var(--text3); font-weight: 400; letter-spacing: 1.5px; text-transform: uppercase; } - - /* ── Navigation ── */ - .nav { - display: flex; - gap: 4px; - padding: 8px 20px; - background: var(--bg); - position: sticky; - top: 73px; - z-index: 49; - } - .nav-btn { - flex: 1; - padding: 10px 8px; - border: none; - background: transparent; - color: var(--text3); - font-family: 'Source Sans 3', sans-serif; - font-size: 13px; - font-weight: 500; - cursor: pointer; - border-radius: var(--radius-sm); - transition: all 0.2s; - letter-spacing: 0.3px; - } - .nav-btn.active { - background: var(--text); - color: var(--bg); - font-weight: 600; - } - .nav-btn:hover:not(.active) { background: var(--bg2); color: var(--text); } - - /* ── Content ── */ - .content { padding: 16px 20px 100px; } - - /* ── Cards ── */ - .card { - background: var(--card); - border-radius: var(--radius); - box-shadow: var(--shadow); - padding: 18px; - margin-bottom: 12px; - border: 1px solid var(--border); - transition: box-shadow 0.2s; - cursor: pointer; - } - .card:hover { box-shadow: var(--shadow-lg); } - - .bean-card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px; } - .bean-name { font-family: 'Playfair Display', serif; font-size: 17px; font-weight: 600; } - .bean-roastery { font-size: 13px; color: var(--text2); margin-top: 2px; } - .bean-meta { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; } - .tag { - font-size: 11px; - padding: 4px 10px; - border-radius: 20px; - background: var(--bg2); - color: var(--text2); - font-weight: 500; - letter-spacing: 0.3px; - } - .tag.roast-light { background: #FFF3D6; color: #8B6914; } - .tag.roast-medium { background: #F0E0C8; color: #6B4E2A; } - .tag.roast-dark { background: #E0D0BD; color: #4A3520; } - - .brew-card { position: relative; } - .brew-method-bar { - position: absolute; - left: 0; - top: 12px; - bottom: 12px; - width: 4px; - border-radius: 0 4px 4px 0; - } - .brew-card .card { padding-left: 24px; } - .brew-date { font-size: 11px; color: var(--text3); letter-spacing: 0.5px; } - .brew-bean { font-size: 14px; font-weight: 600; color: var(--text); margin-top: 2px; } - .brew-method-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; margin-top: 6px; } - .brew-details { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; } - .brew-detail { font-size: 12px; } - .brew-detail-label { color: var(--text3); font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; } - .brew-detail-value { font-weight: 600; margin-top: 1px; } - .brew-notes { font-size: 13px; color: var(--text2); margin-top: 10px; font-style: italic; line-height: 1.5; } - - /* ── Empty State ── */ - .empty { - text-align: center; - padding: 48px 20px; - color: var(--text3); - } - .empty-icon { font-size: 40px; margin-bottom: 12px; opacity: 0.5; } - .empty h3 { font-size: 16px; margin-bottom: 6px; color: var(--text2); } - .empty p { font-size: 13px; line-height: 1.5; } - - /* ── Stats row ── */ - .stats { - display: flex; - gap: 8px; - margin-bottom: 20px; - } - .stat-card { - flex: 1; - background: var(--card); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 14px 12px; - text-align: center; - } - .stat-num { font-family: 'Playfair Display', serif; font-size: 24px; font-weight: 700; } - .stat-label { font-size: 10px; color: var(--text3); text-transform: uppercase; letter-spacing: 1px; margin-top: 2px; } - - /* ── Forms ── */ - .modal-overlay { - position: fixed; - inset: 0; - background: rgba(44,24,16,0.4); - z-index: 100; - display: flex; - align-items: flex-end; - justify-content: center; - animation: fadeIn 0.2s; - } - @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } - @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } } - @keyframes pulse { 0% { opacity: 0.3; } 50% { opacity: 1; } 100% { opacity: 0.3; } } - - .modal { - background: var(--bg); - border-radius: 20px 20px 0 0; - width: 100%; - max-width: 480px; - max-height: 88vh; - overflow-y: auto; - animation: slideUp 0.3s ease-out; - padding: 0 20px 32px; - } - .modal-handle { - width: 36px; - height: 4px; - background: var(--border); - border-radius: 4px; - margin: 12px auto 16px; - } - .modal-title { - font-family: 'Playfair Display', serif; - font-size: 20px; - font-weight: 600; - margin-bottom: 20px; - } - - .form-group { margin-bottom: 16px; } - .form-label { - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.8px; - color: var(--text2); - margin-bottom: 6px; - display: block; - } - .form-input, .form-select, .form-textarea { - width: 100%; - padding: 12px 14px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--card); - font-family: 'Source Sans 3', sans-serif; - font-size: 14px; - color: var(--text); - transition: border-color 0.2s; - outline: none; - } - .form-input:focus, .form-select:focus, .form-textarea:focus { - border-color: var(--accent); - } - .form-textarea { resize: vertical; min-height: 80px; } - .form-row { display: flex; gap: 10px; } - .form-row > .form-group { flex: 1; } - .form-hint { font-size: 11px; color: var(--text3); margin-top: 4px; } - - .method-tabs { - display: flex; - gap: 6px; - margin-bottom: 20px; - } - .method-tab { - flex: 1; - padding: 12px 8px; - border: 2px solid var(--border); - background: var(--card); - border-radius: var(--radius-sm); - cursor: pointer; - text-align: center; - font-family: 'Source Sans 3', sans-serif; - font-size: 12px; - font-weight: 600; - transition: all 0.2s; - letter-spacing: 0.3px; - } - .method-tab.active { border-color: currentColor; } - .method-tab-icon { font-size: 20px; margin-bottom: 4px; } - - .btn { - width: 100%; - padding: 14px; - border: none; - border-radius: var(--radius-sm); - font-family: 'Source Sans 3', sans-serif; - font-size: 14px; - font-weight: 600; - cursor: pointer; - letter-spacing: 0.3px; - transition: opacity 0.2s; - margin-top: 8px; - } - .btn:hover { opacity: 0.9; } - .btn-primary { background: var(--text); color: var(--bg); } - .btn-outline { background: transparent; border: 1px solid var(--border); color: var(--text2); } - .btn-danger { background: transparent; border: 1px solid var(--danger); color: var(--danger); font-size: 12px; padding: 10px; } - - /* ── Image Upload ── */ - .img-upload { - border: 2px dashed var(--border); - border-radius: var(--radius-sm); - padding: 20px; - text-align: center; - cursor: pointer; - transition: border-color 0.2s, background 0.2s; - position: relative; - overflow: hidden; - } - .img-upload:hover { border-color: var(--accent); background: rgba(139,105,20,0.03); } - .img-upload.has-image { padding: 0; border-style: solid; } - .img-upload-icon { font-size: 28px; margin-bottom: 6px; opacity: 0.4; } - .img-upload-text { font-size: 12px; color: var(--text3); } - .img-upload input[type="file"] { - position: absolute; - inset: 0; - opacity: 0; - cursor: pointer; - } - .img-preview { - width: 100%; - height: 160px; - object-fit: cover; - border-radius: var(--radius-sm); - display: block; - } - .img-remove { - position: absolute; - top: 8px; - right: 8px; - width: 28px; - height: 28px; - border-radius: 50%; - background: rgba(44,24,16,0.7); - color: white; - border: none; - font-size: 14px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - } - .bean-card-img { - width: 52px; - height: 52px; - border-radius: var(--radius-sm); - object-fit: cover; - flex-shrink: 0; - } - .bean-detail-img { - width: 100%; - height: 180px; - object-fit: cover; - border-radius: var(--radius); - margin-bottom: 16px; - } - .bean-tasting { font-size: 13px; color: var(--text2); font-style: italic; margin-top: 6px; line-height: 1.4; } - - /* ── FAB ── */ - .fab { - position: fixed; - bottom: 24px; - right: calc(50% - 220px); - width: 54px; - height: 54px; - border-radius: 50%; - background: var(--text); - color: var(--bg); - border: none; - font-size: 26px; - cursor: pointer; - box-shadow: var(--shadow-lg); - z-index: 60; - display: flex; - align-items: center; - justify-content: center; - transition: transform 0.2s; - } - .fab:hover { transform: scale(1.08); } - - .fab-menu { - position: fixed; - bottom: 88px; - right: calc(50% - 220px); - display: flex; - flex-direction: column; - gap: 8px; - z-index: 61; - animation: fadeIn 0.15s; - } - .fab-option { - display: flex; - align-items: center; - gap: 10px; - padding: 10px 16px; - background: var(--card); - border: 1px solid var(--border); - border-radius: 28px; - font-family: 'Source Sans 3', sans-serif; - font-size: 13px; - font-weight: 600; - cursor: pointer; - box-shadow: var(--shadow); - white-space: nowrap; - transition: all 0.2s; - } - .fab-option:hover { box-shadow: var(--shadow-lg); } - - /* ── Section Header ── */ - .section-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 12px; - } - .section-title { font-size: 13px; font-weight: 600; color: var(--text2); text-transform: uppercase; letter-spacing: 1px; } - - /* ── Filter pills ── */ - .filter-pills { - display: flex; - gap: 6px; - margin-bottom: 16px; - overflow-x: auto; - padding-bottom: 4px; - } - .filter-pill { - padding: 6px 14px; - border-radius: 20px; - border: 1px solid var(--border); - background: var(--card); - font-family: 'Source Sans 3', sans-serif; - font-size: 12px; - font-weight: 500; - color: var(--text2); - cursor: pointer; - white-space: nowrap; - transition: all 0.2s; - } - .filter-pill.active { - background: var(--text); - color: var(--bg); - border-color: var(--text); - } - - /* ── Detail view ── */ - .detail-back { - display: flex; - align-items: center; - gap: 6px; - background: none; - border: none; - font-family: 'Source Sans 3', sans-serif; - font-size: 13px; - color: var(--text2); - cursor: pointer; - padding: 0; - margin-bottom: 16px; - } - .detail-section { margin-top: 20px; } -`; +// ─── Shared input/label styles ─── +const inputCls = "w-full px-3.5 py-3 border border-[#E8DFD3] rounded-lg bg-white font-sans text-sm text-[#2C1810] transition-colors outline-none focus:border-[#8B6914]"; +const labelCls = "block text-[10px] font-semibold uppercase tracking-wider text-[#6B5744] mb-1.5"; // ─── Components ─── @@ -518,59 +79,64 @@ function BeanForm({ onSave, onClose, initial }) { }; return ( -
-
e.stopPropagation()}> -
-
{initial ? "Edit Bean" : "Add Bean"}
+
+
e.stopPropagation()}> +
+
{initial ? "Edit Bean" : "Add Bean"}
-
- - set("name", e.target.value)} /> +
+ + set("name", e.target.value)} />
-
- - set("roastery", e.target.value)} /> +
+ + set("roastery", e.target.value)} />
-
-
- - set("roastDate", e.target.value)} /> +
+
+ + set("roastDate", e.target.value)} />
-
- - set("roastType", e.target.value)}> {ROAST_TYPES.map(r => )}
-
- -
+
+ +
{form.image ? ( <> - Bean - + Bean + ) : ( <> -
📷
-
Tap to upload a photo
+
📷
+
Tap to upload a photo
)} - +
-
Max 2 MB · JPG, PNG, or WebP
+
Max 2 MB · JPG, PNG, or WebP
-
- -