From d40b0514fd1d7144dfccf5789c9991a6e7df5eab Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Fri, 24 Apr 2026 15:14:12 +0800 Subject: [PATCH] Refactor styles to utilize Tailwind CSS utility classes for improved consistency and maintainability across forms, motion, shell, and vault components. Remove deprecated reduced-motion styles and consolidate motion-related animations. Update color tokens for better contrast and accessibility. Introduce a new Tailwind CSS configuration file. --- package-lock.json | 962 ++++++++++++++++++++++++++- package.json | 3 + postcss.config.js | 6 + tailwind.config.js | 33 + webapp/src/App.tsx | 44 -- webapp/src/main.tsx | 1 + webapp/src/styles.css | 263 +++++++- webapp/src/styles/auth.css | 63 +- webapp/src/styles/base.css | 13 +- webapp/src/styles/dark.css | 456 +++---------- webapp/src/styles/forms.css | 166 +---- webapp/src/styles/motion.css | 29 +- webapp/src/styles/reduced-motion.css | 32 - webapp/src/styles/responsive.css | 2 +- webapp/src/styles/shell.css | 159 +---- webapp/src/styles/tokens.css | 66 +- webapp/src/styles/vault.css | 132 +--- webapp/src/tailwind.css | 3 + 18 files changed, 1490 insertions(+), 943 deletions(-) create mode 100644 postcss.config.js create mode 100644 tailwind.config.js delete mode 100644 webapp/src/styles/reduced-motion.css create mode 100644 webapp/src/tailwind.css diff --git a/package-lock.json b/package-lock.json index f1dd598..f1f2e62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,12 +25,28 @@ "@cloudflare/workers-types": "^4.20260131.0", "@preact/preset-vite": "^2.10.3", "@types/node": "^25.2.3", + "autoprefixer": "^10.4.21", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.17", "tsx": "^4.21.0", "typescript": "^5.9.3", "vite": "^7.3.1", "wrangler": "^4.71.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -62,7 +78,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -494,8 +509,7 @@ "resolved": "https://registry.npmmirror.com/@cloudflare/workers-types/-/workers-types-4.20260305.0.tgz", "integrity": "sha512-sCgPFnQ03SVpC2OVW8wysONLZW/A8hlp9Mq2ckG/h1oId4kr9NawA6vUiOmOjCWRn2hIohejBYVQ+Vu20rCdKA==", "dev": true, - "license": "MIT OR Apache-2.0", - "peer": true + "license": "MIT OR Apache-2.0" }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", @@ -527,7 +541,6 @@ "resolved": "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", - "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -1591,6 +1604,44 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@poppinss/colors": { "version": "4.1.6", "resolved": "https://registry.npmmirror.com/@poppinss/colors/-/colors-4.1.6.tgz", @@ -2142,7 +2193,6 @@ "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2158,6 +2208,85 @@ "node": ">=18.0.0" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/babel-plugin-transform-hook-names": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz", @@ -2181,6 +2310,19 @@ "node": ">=6.0.0" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/blake3-wasm": { "version": "2.1.5", "resolved": "https://registry.npmmirror.com/blake3-wasm/-/blake3-wasm-2.1.5.tgz", @@ -2195,6 +2337,19 @@ "dev": true, "license": "ISC" }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", @@ -2215,7 +2370,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2230,6 +2384,16 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001774", "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", @@ -2251,6 +2415,54 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2302,6 +2514,19 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", @@ -2330,6 +2555,20 @@ "node": ">=8" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -2419,6 +2658,16 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.27.3", "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.27.3.tgz", @@ -2478,6 +2727,46 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", @@ -2502,6 +2791,33 @@ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "license": "MIT" }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", @@ -2517,6 +2833,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2540,6 +2866,32 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", @@ -2550,6 +2902,78 @@ "he": "bin/he" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2600,6 +3024,26 @@ "dev": true, "license": "MIT" }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2629,6 +3073,43 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/miniflare": { "version": "4.20260301.1", "resolved": "https://registry.npmmirror.com/miniflare/-/miniflare-4.20260301.1.tgz", @@ -2663,6 +3144,18 @@ "dev": true, "license": "MIT" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", @@ -2700,6 +3193,26 @@ "dev": true, "license": "MIT" }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", @@ -2713,6 +3226,33 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -2747,6 +3287,26 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", @@ -2776,12 +3336,138 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/preact": { "version": "10.28.4", "resolved": "https://registry.npmmirror.com/preact/-/preact-10.28.4.tgz", "integrity": "sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -2793,6 +3479,27 @@ "integrity": "sha512-mZSiP6RnbHl4xL2Ap5HfkjLnmxfKcPWpWe/c+5XxCuetEenqmNFf1FH/ftXPCtFG5/TDobjsjz6sSNL0Sr8Z9g==", "license": "MIT" }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/react": { "version": "19.2.4", "resolved": "https://registry.npmmirror.com/react/-/react-19.2.4.tgz", @@ -2816,6 +3523,42 @@ "react": "^19.2.4" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/regexparam": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/regexparam/-/regexparam-3.0.0.tgz", @@ -2825,6 +3568,28 @@ "node": ">=8" } }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -2835,13 +3600,23 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -2881,11 +3656,36 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/semver": { "version": "7.7.4", @@ -2985,6 +3785,29 @@ "node": ">=16" } }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/supports-color": { "version": "10.2.2", "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-10.2.2.tgz", @@ -2998,6 +3821,80 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -3015,6 +3912,26 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", @@ -3027,7 +3944,6 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -3079,7 +3995,6 @@ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "pathe": "^2.0.3" } @@ -3124,13 +4039,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmmirror.com/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -3225,7 +4146,6 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "bin": { "workerd": "bin/workerd" }, @@ -3318,6 +4238,22 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/youch": { "version": "4.1.0-beta.10", "resolved": "https://registry.npmmirror.com/youch/-/youch-4.1.0-beta.10.tgz", diff --git a/package.json b/package.json index b92fd1e..dac0d62 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,9 @@ "@cloudflare/workers-types": "^4.20260131.0", "@preact/preset-vite": "^2.10.3", "@types/node": "^25.2.3", + "autoprefixer": "^10.4.21", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.17", "tsx": "^4.21.0", "typescript": "^5.9.3", "vite": "^7.3.1", diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..f8394f1 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,33 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./webapp/index.html', './webapp/src/**/*.{ts,tsx}'], + darkMode: ['class', '[data-theme="dark"]'], + theme: { + extend: { + colors: { + canvas: 'var(--bg-accent)', + panel: 'var(--panel)', + 'panel-soft': 'var(--panel-soft)', + 'panel-muted': 'var(--panel-muted)', + line: 'var(--line)', + 'line-soft': 'var(--line-soft)', + ink: 'var(--text)', + muted: 'var(--muted)', + 'muted-strong': 'var(--muted-strong)', + brand: 'var(--primary)', + 'brand-hover': 'var(--primary-hover)', + 'brand-strong': 'var(--primary-strong)', + danger: 'var(--danger)', + }, + boxShadow: { + soft: 'var(--shadow-sm)', + panel: 'var(--shadow-md)', + elevated: 'var(--shadow-lg)', + }, + fontFamily: { + sans: ['Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', 'sans-serif'], + }, + }, + }, + plugins: [], +}; diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 5fd2867..4629a4d 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -82,48 +82,6 @@ const SIGNALR_UPDATE_TYPE_DEVICE_STATUS = 12; const SIGNALR_UPDATE_TYPE_BACKUP_RESTORE_PROGRESS = 13; type ThemePreference = 'system' | 'light' | 'dark'; -const MAGNETIC_SELECTOR = '.topbar .btn, .topbar .user-chip, .side-link, .mobile-tab'; - -function installMagneticUiFeedback() { - if (typeof window === 'undefined' || typeof document === 'undefined') return () => {}; - if (typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return () => {}; - if (typeof window.matchMedia === 'function' && window.matchMedia('(pointer: coarse)').matches) return () => {}; - - const resetNode = (node: HTMLElement) => { - node.style.setProperty('--mag-x', '0px'); - node.style.setProperty('--mag-y', '0px'); - node.style.removeProperty('--mx'); - node.style.removeProperty('--my'); - }; - - const onPointerMove = (event: PointerEvent) => { - const node = event.target instanceof Element ? event.target.closest(MAGNETIC_SELECTOR) : null; - if (!node) return; - const rect = node.getBoundingClientRect(); - const localX = event.clientX - rect.left; - const localY = event.clientY - rect.top; - const dx = (localX - rect.width / 2) / Math.max(rect.width / 2, 1); - const dy = (localY - rect.height / 2) / Math.max(rect.height / 2, 1); - node.style.setProperty('--mx', `${localX}px`); - node.style.setProperty('--my', `${localY}px`); - node.style.setProperty('--mag-x', `${dx * 6}px`); - node.style.setProperty('--mag-y', `${dy * 4}px`); - }; - - const onPointerLeave = (event: Event) => { - const node = event.target instanceof Element ? event.target.closest(MAGNETIC_SELECTOR) : null; - if (!node) return; - resetNode(node); - }; - - document.addEventListener('pointermove', onPointerMove, { passive: true }); - document.addEventListener('pointerleave', onPointerLeave, true); - - return () => { - document.removeEventListener('pointermove', onPointerMove); - document.removeEventListener('pointerleave', onPointerLeave, true); - }; -} function readThemePreference(): ThemePreference { if (typeof window === 'undefined') return 'system'; @@ -292,8 +250,6 @@ export default function App() { } }, [phase, profile, session]); - useEffect(() => installMagneticUiFeedback(), []); - function handleToggleTheme() { setThemePreference((prev) => { const current = prev === 'system' ? systemTheme : prev; diff --git a/webapp/src/main.tsx b/webapp/src/main.tsx index 2a0c941..b496154 100644 --- a/webapp/src/main.tsx +++ b/webapp/src/main.tsx @@ -1,6 +1,7 @@ import { render } from 'preact'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import App from './App'; +import './tailwind.css'; import './styles.css'; const queryClient = new QueryClient({ diff --git a/webapp/src/styles.css b/webapp/src/styles.css index b5acc8b..7c4a830 100644 --- a/webapp/src/styles.css +++ b/webapp/src/styles.css @@ -9,4 +9,265 @@ @import './styles/motion.css'; @import './styles/responsive.css'; @import './styles/dark.css'; -@import './styles/reduced-motion.css'; + +/* Unified product polish: clean, flat, quiet surfaces across desktop, mobile, and dark mode. */ +.app-shell, +.auth-card, +.dialog-card, +.card, +.list-panel, +.sidebar-block, +.settings-subcard, +.backup-operations-sidebar, +.backup-destination-sidebar, +.backup-detail-panel, +.restore-progress-card, +.backup-recommendation-card, +.backup-destination-item, +.backup-browser-list, +.backup-browser-path, +.totp-code-row, +.mobile-settings-link, +.table tr { + border-color: var(--line); + border-radius: var(--radius-lg); + background: var(--panel); + box-shadow: var(--shadow-sm); +} + +.app-shell { + background: var(--panel-soft); + border-radius: var(--radius-xl); + box-shadow: var(--shadow-lg); +} + +.topbar, +.mobile-tabbar, +.app-side { + background: var(--panel-soft); + border-color: var(--line-soft); +} + +.brand-logo, +.brand-wordmark, +.standalone-brand-logo, +.standalone-brand-wordmark { + filter: none; +} + +.btn, +.input, +.search-input, +.side-link, +.mobile-tab, +.tree-btn, +.list-item, +.dialog-card, +.mobile-sidebar-sheet, +.mobile-detail-sheet, +.create-menu, +.sort-menu, +.toast-item { + transition-duration: 150ms; +} + +.btn:hover:not(:disabled), +.side-link:hover, +.mobile-tab:hover, +.tree-btn:hover, +.list-item:hover, +.create-menu-item:hover, +.sort-menu-item:hover, +.folder-delete-btn:hover, +.eye-btn:hover, +.password-toggle:hover { + transform: none; +} + +.btn-primary { + background: var(--primary); + border-color: transparent; + color: #ffffff; + box-shadow: none; +} + +.btn-primary:hover { + background: var(--primary-hover); + box-shadow: none; +} + +.btn-secondary, +.btn-danger { + box-shadow: none; +} + +.btn-secondary:hover, +.side-link:hover, +.mobile-tab:hover, +.tree-btn:hover, +.list-item:hover, +.backup-destination-item:hover, +.mobile-settings-link:hover { + background: var(--panel-subtle); +} + +.side-link.active, +.mobile-tab.active, +.tree-btn.active, +.list-item.active, +.sort-menu-item.active, +.backup-destination-item.active, +.backup-interval-preset.active, +.mobile-settings-link.active { + background: color-mix(in srgb, var(--primary) 12%, var(--panel)); + border-color: color-mix(in srgb, var(--primary) 32%, var(--line)); + color: var(--primary-strong); + box-shadow: none; +} + +.list-item::before, +.topbar-actions .btn::before, +.user-chip::before, +.side-link::before, +.mobile-tab::before { + display: none; +} + +.stagger-item { + opacity: 1; + animation: none; +} + +.dialog-mask { + background: rgba(15, 23, 42, 0.42); + backdrop-filter: none; + -webkit-backdrop-filter: none; +} + +.dialog-mask.warning { + background: rgba(15, 23, 42, 0.56); +} + +.dialog-card.warning, +:root[data-theme='dark'] .dialog-card.warning { + background: var(--panel); + border-color: color-mix(in srgb, var(--danger) 28%, var(--line)); + box-shadow: var(--shadow-lg); +} + +.dialog-warning-badge { + border-radius: var(--radius-lg); + background: color-mix(in srgb, var(--danger) 12%, var(--panel)); + color: var(--danger); + box-shadow: none; +} + +.dialog-warning-kicker { + letter-spacing: 0; + text-transform: none; +} + +.dialog-message.warning, +:root[data-theme='dark'] .dialog-message.warning { + background: color-mix(in srgb, var(--danger) 8%, var(--panel)); + border-color: color-mix(in srgb, var(--danger) 20%, var(--line)); + box-shadow: none; + color: var(--text); +} + +.mobile-sidebar-sheet, +.mobile-detail-sheet { + transform: none; + box-shadow: var(--shadow-md); +} + +.mobile-sidebar-sheet.open, +.mobile-detail-sheet.open { + transform: none; +} + +.mobile-fab-trigger { + box-shadow: var(--shadow-md); +} + +.theme-switch-slider, +.theme-switch-input:checked + .theme-switch-slider, +:root[data-theme='dark'] .theme-switch-slider { + background: var(--panel-muted); + border-color: var(--line); +} + +.theme-switch-slider::before, +:root[data-theme='dark'] .theme-switch-slider::before { + background: var(--panel); + box-shadow: var(--shadow-sm); +} + +:root[data-theme='dark'] .app-shell, +:root[data-theme='dark'] .auth-card, +:root[data-theme='dark'] .dialog-card, +:root[data-theme='dark'] .card, +:root[data-theme='dark'] .list-panel, +:root[data-theme='dark'] .sidebar-block, +:root[data-theme='dark'] .settings-subcard, +:root[data-theme='dark'] .backup-operations-sidebar, +:root[data-theme='dark'] .backup-destination-sidebar, +:root[data-theme='dark'] .backup-detail-panel, +:root[data-theme='dark'] .backup-recommendation-card, +:root[data-theme='dark'] .backup-recommendation-dav-item, +:root[data-theme='dark'] .backup-destination-item, +:root[data-theme='dark'] .backup-browser-list, +:root[data-theme='dark'] .backup-browser-path, +:root[data-theme='dark'] .totp-code-row, +:root[data-theme='dark'] .mobile-settings-link, +:root[data-theme='dark'] .table tr, +:root[data-theme='dark'] .list-item, +:root[data-theme='dark'] .input, +:root[data-theme='dark'] .search-input, +:root[data-theme='dark'] .create-menu, +:root[data-theme='dark'] .create-menu-item, +:root[data-theme='dark'] .sort-menu, +:root[data-theme='dark'] .sort-menu-item { + background: var(--panel); + border-color: var(--line); + color: var(--text); + box-shadow: var(--shadow-sm); +} + +:root[data-theme='dark'] .topbar, +:root[data-theme='dark'] .mobile-tabbar, +:root[data-theme='dark'] .app-side { + background: var(--panel-soft); +} + +:root[data-theme='dark'] .btn-secondary { + background: var(--panel); + border-color: var(--line); + color: var(--primary); + box-shadow: none; +} + +:root[data-theme='dark'] .btn-primary { + background: var(--primary); + border-color: transparent; + color: #08111f; + box-shadow: none; +} + +:root[data-theme='dark'] .btn-danger { + background: var(--panel); + border-color: color-mix(in srgb, var(--danger) 36%, var(--line)); + color: var(--danger); + box-shadow: none; +} + +:root[data-theme='dark'] .list-item:hover, +:root[data-theme='dark'] .sort-menu-item:hover, +:root[data-theme='dark'] .create-menu-item:hover, +:root[data-theme='dark'] .mobile-settings-link:hover, +:root[data-theme='dark'] .backup-destination-item:hover, +:root[data-theme='dark'] .side-link:hover, +:root[data-theme='dark'] .mobile-tab:hover, +:root[data-theme='dark'] .tree-btn:hover { + background: var(--panel-subtle); +} diff --git a/webapp/src/styles/auth.css b/webapp/src/styles/auth.css index 6af30d3..24f6a2a 100644 --- a/webapp/src/styles/auth.css +++ b/webapp/src/styles/auth.css @@ -1,19 +1,9 @@ .loading-screen { - height: 100%; - display: grid; - place-items: center; - color: var(--muted); - font-size: 18px; - animation: fade-in-up var(--dur-panel) var(--ease-out-strong) both; + @apply grid h-full place-items-center text-lg text-muted; } .auth-page { - min-height: 100%; - display: grid; - place-items: center; - padding: 24px; - position: relative; - background: transparent; + @apply relative grid min-h-full place-items-center bg-transparent p-6; } .public-send-page { @@ -23,16 +13,9 @@ } .auth-card { - width: 100%; - position: relative; - background: var(--panel); - border: 1px solid var(--line); - border-radius: 24px; - box-shadow: var(--shadow-lg); - padding: 30px; - overflow: hidden; - transform-origin: 50% 24%; - animation: surface-enter 520ms var(--ease-out-strong) both; + @apply relative w-full overflow-hidden border bg-panel p-[30px] shadow-elevated; + border-color: var(--line); + border-radius: 22px; } .auth-card h1 { @@ -41,17 +24,12 @@ } .standalone-shell { + @apply grid gap-3.5; width: min(640px, 100%); - display: grid; - gap: 14px; - animation: fade-in-up 420ms var(--ease-out-strong) both; } .standalone-brand { - display: inline-flex; - align-items: center; - gap: 14px; - margin-bottom: 12px; + @apply mb-3 inline-flex items-center gap-3.5; } .standalone-brand-outside { @@ -61,10 +39,7 @@ } .standalone-brand-logo { - width: 56px; - height: 56px; - object-fit: contain; - flex-shrink: 0; + @apply h-14 w-14 flex-shrink-0 object-contain; filter: drop-shadow(0 8px 18px rgba(43, 102, 217, 0.22)); } @@ -77,11 +52,7 @@ } .standalone-title { - margin: 0 0 4px 0; - text-align: left; - font-size: 31px; - line-height: 1.15; - letter-spacing: -0.035em; + @apply m-0 mb-1 text-left text-3xl font-bold leading-tight tracking-normal; } .standalone-muted { @@ -99,10 +70,7 @@ } .jwt-warning-box { - border: 1px solid #f1d8a5; - border-radius: 12px; - background: #fffaf0; - padding: 12px 14px; + @apply rounded-xl border border-amber-200 bg-amber-50 px-3.5 py-3; } .jwt-warning-label { @@ -157,11 +125,7 @@ } .jwt-generator-actions { - margin-top: 10px; - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; + @apply mt-2.5 flex flex-wrap items-center gap-2.5; } .jwt-copy-hint { @@ -171,10 +135,7 @@ } .standalone-footer { - width: 100%; - text-align: center; - font-size: 13px; - color: #64748b; + @apply w-full text-center text-[13px] text-slate-500; } .standalone-footer a { diff --git a/webapp/src/styles/base.css b/webapp/src/styles/base.css index 1707d52..abeab6b 100644 --- a/webapp/src/styles/base.css +++ b/webapp/src/styles/base.css @@ -1,5 +1,5 @@ * { - box-sizing: border-box; + @apply box-border; } html, @@ -7,21 +7,18 @@ body, #root { margin: 0; padding: 0; - width: 100%; - height: 100%; + @apply h-full w-full; color: var(--text); background: var(--bg-accent); font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', sans-serif; } body { - position: relative; - transition: - background-color var(--dur-medium) var(--ease-smooth), - color var(--dur-medium) var(--ease-smooth); + @apply relative antialiased; + transition: background-color var(--dur-medium) var(--ease-smooth), color var(--dur-medium) var(--ease-smooth); } body.dialog-open { - overflow: hidden; + @apply overflow-hidden; overscroll-behavior: contain; } diff --git a/webapp/src/styles/dark.css b/webapp/src/styles/dark.css index 42303fd..a8e0d8f 100644 --- a/webapp/src/styles/dark.css +++ b/webapp/src/styles/dark.css @@ -2,85 +2,14 @@ :root[data-theme='dark'] #root, :root[data-theme='dark'] .app-page, :root[data-theme='dark'] .auth-page { - background: transparent; + background: var(--bg-accent); color: var(--text); } -:root[data-theme='dark'] .app-shell, -:root[data-theme='dark'] .auth-card, -:root[data-theme='dark'] .dialog, -:root[data-theme='dark'] .jwt-warning-box, -:root[data-theme='dark'] .backup-operations-sidebar, -:root[data-theme='dark'] .backup-destination-sidebar, -:root[data-theme='dark'] .backup-detail-panel, -:root[data-theme='dark'] .settings-subcard, -:root[data-theme='dark'] .list-panel, -:root[data-theme='dark'] .card, -:root[data-theme='dark'] .sidebar-block, -:root[data-theme='dark'] .empty { - background: var(--panel); - border-color: var(--line); - color: var(--text); - box-shadow: var(--shadow-lg); -} - -:root[data-theme='dark'] .topbar, -:root[data-theme='dark'] .mobile-tabbar, -:root[data-theme='dark'] .sort-menu, -:root[data-theme='dark'] .create-menu, -:root[data-theme='dark'] .dialog-card, -:root[data-theme='dark'] .mobile-sidebar-sheet, -:root[data-theme='dark'] .mobile-detail-sheet { - background: var(--panel-soft); - border-color: var(--line); - color: var(--text); -} - -:root[data-theme='dark'] .dialog-card.warning { - border-color: rgba(248, 113, 113, 0.36); - background: linear-gradient(180deg, rgba(39, 16, 16, 0.98), rgba(27, 12, 12, 0.98)); - box-shadow: - 0 36px 90px rgba(5, 5, 5, 0.56), - 0 0 0 1px rgba(248, 113, 113, 0.12) inset; -} - -:root[data-theme='dark'] .dialog-mask.warning { - background: - radial-gradient(circle at top, rgba(127, 29, 29, 0.28), transparent 34%), - linear-gradient(180deg, rgba(20, 12, 12, 0.64), rgba(2, 6, 23, 0.82)); -} - -:root[data-theme='dark'] .dialog-warning-badge { - background: linear-gradient(180deg, rgba(127, 29, 29, 0.8), rgba(69, 10, 10, 0.86)); - color: #fda4af; - box-shadow: - 0 12px 30px rgba(0, 0, 0, 0.32), - 0 0 0 1px rgba(248, 113, 113, 0.14) inset; -} - -:root[data-theme='dark'] .dialog-warning-kicker, -:root[data-theme='dark'] .dialog-card.warning .dialog-title { - color: #fecaca; -} - -:root[data-theme='dark'] .dialog-message.warning { - border-color: rgba(248, 113, 113, 0.18); - background: linear-gradient(180deg, rgba(69, 10, 10, 0.54), rgba(67, 20, 7, 0.46)); - color: #fecdd3; - box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18) inset; -} - -:root[data-theme='dark'] .app-side, -:root[data-theme='dark'] .sidebar, -:root[data-theme='dark'] .mobile-sidebar-sheet .sidebar-block { - background: var(--panel-muted); - border-color: var(--line); -} - -:root[data-theme='dark'] .auth-card { - background: var(--panel); -} - +:root[data-theme='dark'] h1, +:root[data-theme='dark'] h2, +:root[data-theme='dark'] h3, +:root[data-theme='dark'] h4, :root[data-theme='dark'] .brand, :root[data-theme='dark'] .mobile-page-title, :root[data-theme='dark'] .detail-title, @@ -89,277 +18,6 @@ :root[data-theme='dark'] .kv-main strong, :root[data-theme='dark'] .list-title, :root[data-theme='dark'] .sidebar-title, -:root[data-theme='dark'] h1, -:root[data-theme='dark'] h2, -:root[data-theme='dark'] h3, -:root[data-theme='dark'] h4 { - color: var(--text); -} - -:root[data-theme='dark'] .standalone-brand-wordmark, -:root[data-theme='dark'] .brand-wordmark { - text-shadow: 0 16px 28px rgba(2, 6, 23, 0.32); -} - -:root[data-theme='dark'] .muted, -:root[data-theme='dark'] .detail-sub, -:root[data-theme='dark'] .field-help, -:root[data-theme='dark'] .list-sub, -:root[data-theme='dark'] .kv-label, -:root[data-theme='dark'] .standalone-muted, -:root[data-theme='dark'] .standalone-footer, -:root[data-theme='dark'] .backup-inline-note, -:root[data-theme='dark'] .backup-browser-empty, -:root[data-theme='dark'] .or, -:root[data-theme='dark'] .mobile-tab, -:root[data-theme='dark'] .side-link, -:root[data-theme='dark'] .user-chip, -:root[data-theme='dark'] .list-count { - color: var(--muted); -} - -:root[data-theme='dark'] .user-chip { - background: rgba(17, 34, 56, 0.94); - border-color: var(--line); - box-shadow: 0 12px 24px rgba(1, 7, 18, 0.24); -} - -:root[data-theme='dark'] .side-link:hover, -:root[data-theme='dark'] .mobile-tab:hover { - background: rgba(132, 182, 255, 0.11); -} - -:root[data-theme='dark'] .side-link.active, -:root[data-theme='dark'] .mobile-tab.active, -:root[data-theme='dark'] .sort-menu-item.active, -:root[data-theme='dark'] .list-item.active { - background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.08)); - border-color: rgba(132, 182, 255, 0.28); - color: var(--primary); -} - -:root[data-theme='dark'] .input, -:root[data-theme='dark'] .textarea, -:root[data-theme='dark'] select.input, -:root[data-theme='dark'] .dialog input, -:root[data-theme='dark'] .dialog textarea, -:root[data-theme='dark'] .dialog select { - background: rgba(13, 24, 40, 0.94); - border-color: rgba(103, 136, 186, 0.36); - color: var(--text); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); -} - -:root[data-theme='dark'] .input::placeholder, -:root[data-theme='dark'] .textarea::placeholder, -:root[data-theme='dark'] input::placeholder, -:root[data-theme='dark'] textarea::placeholder { - color: #7488a8; -} - -:root[data-theme='dark'] .input:focus, -:root[data-theme='dark'] .textarea:focus, -:root[data-theme='dark'] .search-input:focus, -:root[data-theme='dark'] .dialog input:focus, -:root[data-theme='dark'] .dialog textarea:focus, -:root[data-theme='dark'] .dialog select:focus { - border-color: rgba(132, 182, 255, 0.54); - background-color: rgba(16, 30, 49, 0.98); - box-shadow: 0 0 0 4px rgba(132, 182, 255, 0.12), 0 10px 22px rgba(5, 13, 28, 0.24), inset 0 1px 0 rgba(255, 255, 255, 0.04); -} -:root[data-theme='dark'] .input-readonly { - background: #0f1b2d; - color: var(--muted-strong); -} - -:root[data-theme='dark'] .input:disabled, -:root[data-theme='dark'] .btn:disabled { - background: #132033; - border-color: #22334c; - color: #70829d; -} - -:root[data-theme='dark'] .btn-secondary { - background: linear-gradient(180deg, rgba(22, 41, 66, 0.98), rgba(16, 31, 52, 0.98)); - border-color: rgba(132, 182, 255, 0.22); - color: #a9cdff; - box-shadow: 0 12px 22px rgba(1, 7, 18, 0.18); -} - -:root[data-theme='dark'] .btn-secondary:hover { - background: linear-gradient(180deg, rgba(26, 49, 79, 0.98), rgba(19, 37, 61, 0.98)); - border-color: rgba(132, 182, 255, 0.3); -} - -:root[data-theme='dark'] .btn-danger { - background: linear-gradient(180deg, rgba(45, 23, 33, 0.98), rgba(35, 18, 28, 0.98)); - border-color: rgba(255, 139, 168, 0.38); - color: #ff9bb0; -} - -:root[data-theme='dark'] .btn-danger:hover { - background: linear-gradient(180deg, rgba(56, 27, 40, 0.98), rgba(41, 19, 31, 0.98)); - border-color: rgba(255, 171, 192, 0.42); -} - -:root[data-theme='dark'] .btn-primary { - background: linear-gradient(135deg, #79acff, #57c2ff 76%); - border-color: rgba(176, 214, 255, 0.22); - color: #061120; - box-shadow: 0 18px 32px rgba(10, 26, 52, 0.34); -} - -:root[data-theme='dark'] .btn-primary:hover { - background: linear-gradient(135deg, #90bcff, #6accff 76%); - box-shadow: 0 22px 36px rgba(10, 26, 52, 0.38); -} - -:root[data-theme='dark'] .toolbar.actions, -:root[data-theme='dark'] .list-head, -:root[data-theme='dark'] .mobile-panel-head, -:root[data-theme='dark'] .backup-recommendation-header, -:root[data-theme='dark'] .backup-operations-sidebar, -:root[data-theme='dark'] .backup-destination-sidebar, -:root[data-theme='dark'] .backup-detail-panel, -:root[data-theme='dark'] .detail-actions, -:root[data-theme='dark'] .topbar, -:root[data-theme='dark'] .app-side, -:root[data-theme='dark'] .kv-row, -:root[data-theme='dark'] .attachment-row, -:root[data-theme='dark'] .backup-browser-row { - border-color: var(--line); -} - -:root[data-theme='dark'] .input, -:root[data-theme='dark'] .search-input, -:root[data-theme='dark'] .list-item, -:root[data-theme='dark'] .sidebar-block { - background: rgba(15, 28, 45, 0.94); -} - -:root[data-theme='dark'] .sidebar, -:root[data-theme='dark'] .content, -:root[data-theme='dark'] .list-col, -:root[data-theme='dark'] .detail-col { - color: var(--text); -} - -:root[data-theme='dark'] .mobile-sidebar-mask, -:root[data-theme='dark'] .dialog-mask { - background: var(--overlay-strong); -} - -:root[data-theme='dark'] .toast { - background: linear-gradient(180deg, rgba(19, 34, 54, 0.98), rgba(14, 26, 42, 0.98)); - border-color: #263a57; - color: var(--text); -} - -:root[data-theme='dark'] .toast.success { - background: #0f2a1f; - border-color: #1f5b44; - color: #9be2bd; -} - -:root[data-theme='dark'] .toast.error { - background: #2a1720; - border-color: #6c2b41; - color: #ffb1c0; -} - -:root[data-theme='dark'] .toast.warning { - background: #2d2413; - border-color: #7b6230; - color: #f7d48b; -} - -:root[data-theme='dark'] .jwt-warning-head, -:root[data-theme='dark'] .jwt-warning-label, -:root[data-theme='dark'] .jwt-warning-copy, -:root[data-theme='dark'] .jwt-warning-list { - color: #f4d48a; -} - -:root[data-theme='dark'] .theme-switch-input:focus + .theme-switch-slider { - box-shadow: 0 0 0 2px rgba(132, 182, 255, 0.24); -} - -:root[data-theme='dark'] .search-input, -:root[data-theme='dark'] .list-head .search-input, -:root[data-theme='dark'] .mobile-settings-card, -:root[data-theme='dark'] .mobile-settings-link, -:root[data-theme='dark'] .table tr, -:root[data-theme='dark'] .settings-subcard, -:root[data-theme='dark'] .backup-operations-sidebar, -:root[data-theme='dark'] .backup-destination-sidebar, -:root[data-theme='dark'] .backup-detail-panel, -:root[data-theme='dark'] .dialog-card, -:root[data-theme='dark'] .backup-browser-path, -:root[data-theme='dark'] .backup-browser-list, -:root[data-theme='dark'] .create-menu, -:root[data-theme='dark'] .create-menu-item, -:root[data-theme='dark'] .sort-menu, -:root[data-theme='dark'] .sort-menu-item, -:root[data-theme='dark'] .backup-recommendation-card, -:root[data-theme='dark'] .backup-recommendation-dav-item, -:root[data-theme='dark'] .backup-destination-item, -:root[data-theme='dark'] .totp-code-row, -:root[data-theme='dark'] .list-item { - background: - linear-gradient(180deg, rgba(18, 32, 52, 0.92), rgba(14, 26, 42, 0.92)); - border-color: var(--line); - color: var(--text); -} - -:root[data-theme='dark'] .list-item:hover, -:root[data-theme='dark'] .sort-menu-item:hover, -:root[data-theme='dark'] .create-menu-item:hover, -:root[data-theme='dark'] .mobile-settings-link:hover, -:root[data-theme='dark'] .backup-destination-item:hover { - background: - linear-gradient(180deg, rgba(24, 44, 70, 0.96), rgba(16, 31, 51, 0.96)); - border-color: rgba(118, 150, 197, 0.32); -} - -:root[data-theme='dark'] .list-item.active { - background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.1)); - border-color: rgba(122, 176, 255, 0.34); - box-shadow: inset 0 0 0 1px rgba(200, 225, 255, 0.06), 0 12px 24px rgba(5, 13, 28, 0.18); -} - -:root[data-theme='dark'] .list-item::before { - background: - linear-gradient(90deg, rgba(132, 182, 255, 0.08), transparent 24%, transparent 76%, rgba(56, 189, 248, 0.08)), - radial-gradient(circle at 18px 50%, rgba(255, 255, 255, 0.06), transparent 44%); -} - -:root[data-theme='dark'] .backup-destination-item.active, -:root[data-theme='dark'] .backup-interval-preset.active, -:root[data-theme='dark'] .mobile-settings-link.active, -:root[data-theme='dark'] .tree-btn.active { - background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.1)); - border-color: rgba(132, 182, 255, 0.34); - color: #f4f8ff; -} - -:root[data-theme='dark'] .theme-switch-slider { - background: linear-gradient(180deg, #1d3659, #142845); - border-color: rgba(120, 152, 198, 0.34); -} - -:root[data-theme='dark'] .theme-switch-slider::before { - background: linear-gradient(180deg, #f8fbff, #dce9ff); - box-shadow: 0 3px 10px rgba(2, 8, 20, 0.28); -} - -:root[data-theme='dark'] .theme-switch .moon svg { - fill: #8db6ff; -} - -:root[data-theme='dark'] .theme-switch .sun svg { - opacity: 0.82; -} - :root[data-theme='dark'] .totp-code-name, :root[data-theme='dark'] .backup-destination-name, :root[data-theme='dark'] .backup-browser-entry, @@ -376,6 +34,20 @@ color: var(--text); } +:root[data-theme='dark'] .muted, +:root[data-theme='dark'] .detail-sub, +:root[data-theme='dark'] .field-help, +:root[data-theme='dark'] .list-sub, +:root[data-theme='dark'] .kv-label, +:root[data-theme='dark'] .standalone-muted, +:root[data-theme='dark'] .standalone-footer, +:root[data-theme='dark'] .backup-inline-note, +:root[data-theme='dark'] .backup-browser-empty, +:root[data-theme='dark'] .or, +:root[data-theme='dark'] .mobile-tab, +:root[data-theme='dark'] .side-link, +:root[data-theme='dark'] .user-chip, +:root[data-theme='dark'] .list-count, :root[data-theme='dark'] .totp-code-username, :root[data-theme='dark'] .backup-destination-meta, :root[data-theme='dark'] .backup-browser-meta, @@ -385,67 +57,89 @@ :root[data-theme='dark'] .backup-recommendation-linked-item, :root[data-theme='dark'] .backup-inline-suffix, :root[data-theme='dark'] .folder-delete-btn, -:root[data-theme='dark'] .folder-add-btn:hover, -:root[data-theme='dark'] .tree-label, -:root[data-theme='dark'] .list-sub { +:root[data-theme='dark'] .tree-label { color: var(--muted); } -:root[data-theme='dark'] .import-export-panel p, -:root[data-theme='dark'] .dialog-message, -:root[data-theme='dark'] .local-error, -:root[data-theme='dark'] .status-ok { - color: var(--muted); +:root[data-theme='dark'] .input, +:root[data-theme='dark'] .textarea, +:root[data-theme='dark'] select.input, +:root[data-theme='dark'] .search-input, +:root[data-theme='dark'] .dialog input, +:root[data-theme='dark'] .dialog textarea, +:root[data-theme='dark'] .dialog select { + background: var(--panel); + border-color: var(--line); + color: var(--text); + box-shadow: none; } -:root[data-theme='dark'] .backup-destination-type { - background: #1d3048; - color: #c9d8eb; +:root[data-theme='dark'] .input::placeholder, +:root[data-theme='dark'] .textarea::placeholder, +:root[data-theme='dark'] input::placeholder, +:root[data-theme='dark'] textarea::placeholder { + color: color-mix(in srgb, var(--muted) 76%, transparent); } -:root[data-theme='dark'] .backup-help-trigger { - border-color: #38618f; - background: #173150; - color: #9ec5ff; +:root[data-theme='dark'] .input:focus, +:root[data-theme='dark'] .textarea:focus, +:root[data-theme='dark'] .search-input:focus, +:root[data-theme='dark'] .dialog input:focus, +:root[data-theme='dark'] .dialog textarea:focus, +:root[data-theme='dark'] .dialog select:focus { + border-color: color-mix(in srgb, var(--primary) 54%, var(--line)); + background: var(--panel); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 16%, transparent); } -:root[data-theme='dark'] .backup-help-trigger:hover, -:root[data-theme='dark'] .backup-help-trigger:focus-visible { - border-color: #5f92d7; - background: #20426a; +:root[data-theme='dark'] .input-readonly { + background: var(--panel-muted); + color: var(--muted-strong); } -:root[data-theme='dark'] .backup-help-bubble { +:root[data-theme='dark'] .input:disabled, +:root[data-theme='dark'] .btn:disabled { + background: var(--panel-muted); + border-color: var(--line-soft); + color: color-mix(in srgb, var(--muted) 62%, transparent); +} + +:root[data-theme='dark'] .mobile-sidebar-mask, +:root[data-theme='dark'] .dialog-mask { + background: var(--overlay-strong); +} + +:root[data-theme='dark'] .toast-item { background: var(--panel); border-color: var(--line); color: var(--text); } -:root[data-theme='dark'] .backup-help-bubble::before { - background: var(--panel); - border-left-color: var(--line); - border-top-color: var(--line); +:root[data-theme='dark'] .toast-item.error, +:root[data-theme='dark'] .toast-item.warning { + border-color: color-mix(in srgb, var(--danger) 36%, var(--line)); + background: color-mix(in srgb, var(--danger) 12%, var(--panel)); + color: var(--text); } -:root[data-theme='dark'] .table td { - border-bottom-color: #203047; +:root[data-theme='dark'] .jwt-warning-head, +:root[data-theme='dark'] .jwt-warning-label, +:root[data-theme='dark'] .jwt-warning-copy, +:root[data-theme='dark'] .jwt-warning-list { + color: var(--warning); } :root[data-theme='dark'] .local-error { - color: #ff9bb0; + color: var(--danger); } :root[data-theme='dark'] .status-ok { - color: #9be2bd; -} - -:root[data-theme='dark'] .totp-qr { - background: #ffffff; - border-color: rgba(15, 23, 42, 0.12); + color: var(--success); } +:root[data-theme='dark'] .totp-qr, :root[data-theme='dark'] .totp-qr svg, :root[data-theme='dark'] .totp-qr img { background: #ffffff; - border-radius: 8px; + border-color: rgba(15, 23, 42, 0.12); } diff --git a/webapp/src/styles/forms.css b/webapp/src/styles/forms.css index 1a076c9..b34aa89 100644 --- a/webapp/src/styles/forms.css +++ b/webapp/src/styles/forms.css @@ -1,38 +1,20 @@ .muted { - margin: 0 0 16px 0; - text-align: center; - color: var(--muted); - line-height: 1.65; + @apply m-0 mb-4 text-center leading-relaxed text-muted; } .field { - display: block; - margin-bottom: 14px; + @apply mb-3.5 block; } .field > span { - display: block; - margin-bottom: 8px; - font-size: 14px; - font-weight: 600; + @apply mb-2 block text-sm font-semibold; } .input { - width: 100%; - height: 48px; - border: 1px solid rgba(74, 103, 150, 0.42); - border-radius: 14px; - padding: 10px 14px; - font-size: 16px; - outline: none; - color: var(--text); + @apply h-12 w-full rounded-xl border px-3.5 py-2.5 text-base text-ink outline-none transition; background: var(--panel); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9); - transition: - border-color var(--dur-fast) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - background-color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft); + border-color: rgba(74, 103, 150, 0.34); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82); } select.input { @@ -59,15 +41,10 @@ input[type='file'].input { } input[type='file'].input::file-selector-button { - height: 32px; - border: 1px solid #3f5b9e; - border-radius: 999px; - padding: 0 12px; + @apply mr-2.5 h-8 cursor-pointer rounded-full border px-3 font-bold; background: #eef4ff; + border-color: #9db8ea; color: #1f4ea0; - font-weight: 700; - cursor: pointer; - margin-right: 10px; } input[type='file'].input::file-selector-button:hover { @@ -76,32 +53,25 @@ input[type='file'].input::file-selector-button:hover { } .textarea { - min-height: 110px; - height: auto; - resize: vertical; + @apply h-auto min-h-28 resize-y; } .input:focus { border-color: rgba(43, 102, 217, 0.6); background-color: #fbfdff; - box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.11), 0 10px 20px rgba(37, 99, 235, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.95); - transform: translateY(-1px); + box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.10), 0 8px 18px rgba(37, 99, 235, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.95); } .input-readonly { - background: #eef2f7; - color: #475569; + @apply bg-slate-100 text-slate-600; } .input:disabled { - background: #e2e8f0; - border-color: #cbd5e1; - color: #94a3b8; - cursor: not-allowed; + @apply cursor-not-allowed border-slate-300 bg-slate-200 text-slate-400; } .password-wrap { - position: relative; + @apply relative; } .password-wrap .input { @@ -109,32 +79,12 @@ input[type='file'].input::file-selector-button:hover { } .password-toggle { - position: absolute; - right: 8px; - top: 50%; + @apply absolute right-2 top-1/2 grid cursor-pointer place-items-center border-0 bg-transparent text-blue-700 transition; transform: translateY(-50%); - border: none; - background: transparent; - color: #275ac2; - cursor: pointer; - display: grid; - place-items: center; - transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft); } .eye-btn { - position: absolute; - right: 10px; - bottom: 9px; - width: 30px; - height: 30px; - border: none; - background: transparent; - cursor: pointer; - display: grid; - place-items: center; - color: #334155; - transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft); + @apply absolute bottom-2.5 right-2.5 grid h-8 w-8 cursor-pointer place-items-center border-0 bg-transparent text-slate-700 transition; } .password-toggle:hover, @@ -144,33 +94,13 @@ input[type='file'].input::file-selector-button:hover { } .btn { - height: 36px; - border: 1px solid transparent; - border-radius: 999px; - padding: 0 16px; - font-size: 15px; - font-weight: 700; - cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - text-decoration: none; - transition: - transform var(--dur-fast) var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft), - background-color var(--dur-fast) var(--ease-smooth), - border-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - opacity var(--dur-fast) var(--ease-smooth); + @apply inline-flex h-9 cursor-pointer items-center justify-center gap-1.5 rounded-full border border-transparent px-4 text-[15px] font-bold no-underline transition; } .topbar-actions .btn, .user-chip, .side-link, .mobile-tab { - --mag-x: 0px; - --mag-y: 0px; position: relative; overflow: hidden; } @@ -181,8 +111,8 @@ input[type='file'].input::file-selector-button:hover { .mobile-tab::before { content: ''; position: absolute; - left: var(--mx, 50%); - top: var(--my, 50%); + left: 50%; + top: 50%; width: 110px; height: 110px; border-radius: 999px; @@ -199,12 +129,11 @@ input[type='file'].input::file-selector-button:hover { .user-chip:hover::before, .side-link:hover::before, .mobile-tab:hover::before { - opacity: 1; - transform: translate(-50%, -50%) scale(1); + opacity: 0; } .btn:hover:not(:disabled) { - transform: translateY(-2px) scale(1.01); + transform: translateY(-1px); } .btn:active:not(:disabled) { @@ -216,30 +145,23 @@ input[type='file'].input::file-selector-button:hover { } .btn.full { - width: 100%; - height: 50px; - font-size: 22px; - margin: 10px 0; + @apply my-2.5 h-12 w-full text-lg; } .btn-primary { - background: linear-gradient(135deg, #2563eb, #3b82f6 72%); - border-color: rgba(15, 63, 152, 0.32); - color: #fff; - box-shadow: 0 14px 28px rgba(37, 99, 235, 0.24); + @apply border-blue-700/30 bg-blue-600 text-white; + box-shadow: 0 10px 22px rgba(37, 99, 235, 0.20); } .btn-primary:hover { - background: linear-gradient(135deg, #1d4ed8, #3377f0 72%); - border-color: rgba(15, 63, 152, 0.38); - box-shadow: 0 18px 34px rgba(37, 99, 235, 0.28); + @apply bg-blue-700; + box-shadow: 0 12px 26px rgba(37, 99, 235, 0.22); } .btn-secondary { - background: var(--panel); - border-color: rgba(37, 99, 235, 0.22); - color: var(--primary-strong); - box-shadow: 0 8px 18px rgba(13, 31, 68, 0.05); + @apply bg-panel text-brand-strong; + border-color: rgba(37, 99, 235, 0.20); + box-shadow: 0 6px 14px rgba(13, 31, 68, 0.04); } .btn-secondary:hover { @@ -248,9 +170,8 @@ input[type='file'].input::file-selector-button:hover { } .btn-danger { - background: rgba(255, 255, 255, 0.8); + @apply bg-white/80 text-danger; border-color: rgba(217, 45, 87, 0.28); - color: var(--danger); } .btn-danger:hover { @@ -259,42 +180,23 @@ input[type='file'].input::file-selector-button:hover { } .btn:disabled { - background: #e2e8f0; - border-color: #cbd5e1; - color: #94a3b8; - cursor: not-allowed; + @apply cursor-not-allowed border-slate-300 bg-slate-200 text-slate-400; } .or { - text-align: center; - margin: 10px 0; - color: #334155; + @apply my-2.5 text-center text-slate-700; } .field-help { - margin-top: 8px; - font-size: 13px; - line-height: 1.5; - color: #667085; + @apply mt-2 text-[13px] leading-normal text-slate-500; } .auth-support-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - margin: -2px 0 12px; + @apply -mt-0.5 mb-3 flex items-center justify-between gap-2.5; } .auth-link-btn { - border: none; - background: transparent; - padding: 0; - color: #1d4ed8; - font-size: 13px; - font-weight: 700; - cursor: pointer; - transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); + @apply cursor-pointer border-0 bg-transparent p-0 text-[13px] font-bold text-blue-700 transition; } .auth-link-btn:hover { diff --git a/webapp/src/styles/motion.css b/webapp/src/styles/motion.css index ab6299c..073672a 100644 --- a/webapp/src/styles/motion.css +++ b/webapp/src/styles/motion.css @@ -21,77 +21,63 @@ @keyframes fade-in-up { from { opacity: 0; - transform: translate3d(0, 16px, 0); } to { opacity: 1; - transform: translate3d(0, 0, 0); } } @keyframes shell-enter { from { opacity: 0; - transform: translate3d(0, 18px, 0) scale(0.992); } to { opacity: 1; - transform: translate3d(0, 0, 0) scale(1); } } @keyframes surface-enter { from { opacity: 0; - transform: translate3d(0, 20px, 0) scale(0.985); } to { opacity: 1; - transform: translate3d(0, 0, 0) scale(1); } } @keyframes menu-in { from { opacity: 0; - transform: translate3d(0, 10px, 0) scale(0.96); } to { opacity: 1; - transform: translate3d(0, 0, 0) scale(1); } } @keyframes dialog-in { from { opacity: 0; - transform: translate3d(0, 18px, 0) scale(0.96); } to { opacity: 1; - transform: translate3d(0, 0, 0) scale(1); } } @keyframes toast-in { from { opacity: 0; - transform: translate3d(18px, 0, 0) scale(0.97); } to { opacity: 1; - transform: translate3d(0, 0, 0) scale(1); } } @keyframes stagger-rise { from { opacity: 0; - transform: translate3d(0, 18px, 0) scale(0.985); } to { opacity: 1; - transform: translate3d(0, 0, 0) scale(1); } } @@ -107,21 +93,28 @@ @keyframes dialog-out { from { opacity: 1; - transform: translate3d(0, 0, 0) scale(1); } to { opacity: 0; - transform: translate3d(0, 10px, 0) scale(0.972); } } @keyframes route-stage-in { from { opacity: 0; - transform: translate3d(0, 14px, 0); } to { opacity: 1; - transform: translate3d(0, 0, 0); + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 1ms !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + transition-duration: 1ms !important; } } diff --git a/webapp/src/styles/reduced-motion.css b/webapp/src/styles/reduced-motion.css deleted file mode 100644 index dc2ddb6..0000000 --- a/webapp/src/styles/reduced-motion.css +++ /dev/null @@ -1,32 +0,0 @@ -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; - } - - *, - *::before, - *::after { - animation-duration: 1ms !important; - animation-iteration-count: 1 !important; - transition-duration: 1ms !important; - scroll-behavior: auto !important; - } - - .btn:hover:not(:disabled), - .btn:active:not(:disabled), - .side-link:hover, - .tree-btn:hover, - .list-item:hover, - .list-item.active, - .search-input:focus, - .input:focus, - .password-toggle:hover, - .eye-btn:hover, - .auth-link-btn:hover, - .sort-menu-item:hover, - .create-menu-item:hover, - .toast-close:hover, - .mobile-sidebar-close:hover { - transform: none !important; - } -} diff --git a/webapp/src/styles/responsive.css b/webapp/src/styles/responsive.css index 32119fa..7c89014 100644 --- a/webapp/src/styles/responsive.css +++ b/webapp/src/styles/responsive.css @@ -213,7 +213,7 @@ } .mobile-tab:hover { - transform: translate3d(var(--mag-x), calc(var(--mag-y) - 1px), 0); + transform: translateY(-1px); } .mobile-tab.active { diff --git a/webapp/src/styles/shell.css b/webapp/src/styles/shell.css index da3271e..70d49ef 100644 --- a/webapp/src/styles/shell.css +++ b/webapp/src/styles/shell.css @@ -1,44 +1,22 @@ .app-page { - min-height: 100%; - padding: 20px; - position: relative; - background: transparent; + @apply relative min-h-full bg-transparent p-5; } .app-shell { + @apply relative mx-auto flex max-w-[1600px] flex-col overflow-hidden border bg-panel-soft shadow-elevated; height: calc(100vh - 40px); - max-width: 1600px; - margin: 0 auto; - position: relative; - background: var(--panel-soft); - border: 1px solid var(--line); - border-radius: 28px; - box-shadow: var(--shadow-lg); - display: flex; - flex-direction: column; - overflow: hidden; - animation: shell-enter 560ms var(--ease-out-strong) both; + border-color: var(--line); + border-radius: 24px; } .topbar { - height: 58px; - border-bottom: 1px solid var(--line-soft); - color: #0f172a; - background: rgba(244, 248, 255, 0.72); - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 18px; - transition: background-color var(--dur-fast) var(--ease-smooth), border-color var(--dur-fast) var(--ease-smooth); + @apply flex h-[58px] items-center justify-between border-b px-[18px] text-slate-900 transition; + border-color: var(--line-soft); + background: rgba(244, 248, 255, 0.82); } .brand { - display: inline-flex; - align-items: center; - gap: 8px; - font-size: 34px; - font-weight: 800; - color: var(--text); + @apply inline-flex items-center gap-2 text-[34px] font-extrabold text-ink; } .brand-wordmark { @@ -50,30 +28,17 @@ } .mobile-page-title { - display: none; - min-width: 0; + @apply hidden min-w-0 overflow-hidden text-ellipsis whitespace-nowrap text-[19px] font-extrabold leading-tight text-slate-900; max-width: min(58vw, 240px); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 19px; - line-height: 1.2; - font-weight: 800; - color: #0f172a; } .brand-logo { - width: 42px; - height: 42px; - object-fit: contain; + @apply h-[42px] w-[42px] object-contain; filter: drop-shadow(0 10px 22px rgba(43, 102, 217, 0.22)); - transition: transform var(--dur-medium) var(--ease-out-soft), filter var(--dur-medium) var(--ease-out-soft); } .topbar-actions { - display: flex; - align-items: center; - gap: 10px; + @apply flex items-center gap-2.5; } .mobile-tabbar { @@ -93,9 +58,7 @@ } .theme-switch-wrap { - display: inline-flex; - align-items: center; - justify-content: center; + @apply inline-flex items-center justify-center; } .theme-switch { @@ -112,20 +75,9 @@ } .theme-switch-slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(180deg, #dceaff, #c8dcff); - border: 1px solid #9dbbec; - transition: - background var(--dur-medium) var(--ease-out-soft), - border-color var(--dur-medium) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - transform var(--dur-fast) var(--ease-out-soft); - border-radius: 999px; + @apply absolute inset-0 cursor-pointer rounded-full border transition; + background: #dbeafe; + border-color: #9dbbec; } .theme-switch-slider::before { @@ -182,7 +134,7 @@ } .theme-switch:hover .theme-switch-slider { - transform: scale(1.02); + transform: none; } .theme-switch:hover .sun svg, @@ -196,105 +148,58 @@ padding: 0 12px; font-size: 13px; font-weight: 600; - transform: translate3d(var(--mag-x), var(--mag-y), 0); transition-duration: 220ms; } .topbar-actions .btn:hover:not(:disabled) { - transform: translate3d(var(--mag-x), calc(var(--mag-y) - 2px), 0) scale(1.02); + transform: translateY(-1px); } .user-chip { - display: inline-flex; - align-items: center; - gap: 6px; - height: 34px; - border-radius: 999px; - padding: 0 12px; - border: 1px solid rgba(148, 163, 184, 0.3); - background: rgba(249, 251, 255, 0.92); - color: var(--muted-strong); - font-size: 14px; - font-weight: 600; - box-shadow: 0 10px 18px rgba(13, 31, 68, 0.05); - transform: translate3d(var(--mag-x), var(--mag-y), 0); - transition: - transform 220ms var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft), - border-color var(--dur-fast) var(--ease-smooth), - background-color var(--dur-fast) var(--ease-smooth); + @apply inline-flex h-[34px] items-center gap-1.5 rounded-full border px-3 text-sm font-semibold text-muted-strong transition; + background: rgba(249, 251, 255, 0.94); + border-color: rgba(148, 163, 184, 0.30); + box-shadow: 0 8px 16px rgba(13, 31, 68, 0.04); } .user-chip:hover { - transform: translate3d(var(--mag-x), calc(var(--mag-y) - 1px), 0); - box-shadow: 0 16px 28px rgba(15, 23, 42, 0.08); + box-shadow: 0 10px 22px rgba(15, 23, 42, 0.06); } .app-main { - flex: 1; - min-height: 0; - display: grid; + @apply grid min-h-0 flex-1; grid-template-columns: 200px 1fr; } .app-side { - border-right: 1px solid var(--line-soft); - padding: 16px 12px; - display: flex; - flex-direction: column; - gap: 8px; + @apply flex flex-col gap-2 border-r px-3 py-4; + border-color: var(--line-soft); } .side-link { - display: flex; - align-items: center; - gap: 10px; - padding: 11px 12px; - border-radius: 14px; - color: var(--muted-strong); - text-decoration: none; - border: 1px solid transparent; - font-weight: 600; - font-size: 14px; - transition: - background-color var(--dur-fast) var(--ease-smooth), - border-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft); + @apply flex items-center gap-2.5 rounded-xl border border-transparent px-3 py-2.5 text-sm font-semibold text-muted-strong no-underline transition; } .side-link:hover { - background: #ffffff; + background: #fff; border-color: rgba(128, 152, 192, 0.18); color: var(--text); - transform: translate3d(calc(var(--mag-x) + 3px), var(--mag-y), 0); - box-shadow: 0 14px 24px rgba(15, 23, 42, 0.05); + box-shadow: 0 10px 18px rgba(15, 23, 42, 0.04); } .side-link.active { - background: linear-gradient(135deg, rgba(37, 99, 235, 0.18), rgba(59, 130, 246, 0.08)); + background: rgba(37, 99, 235, 0.11); border-color: rgba(37, 99, 235, 0.28); color: var(--primary-strong); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.64), 0 10px 18px rgba(37, 99, 235, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.58); } .content { - min-height: 0; - padding: 14px; - overflow: hidden; + @apply min-h-0 overflow-hidden p-3.5; } .route-stage { - height: 100%; - min-height: 0; - overflow: auto; -} - -@media (min-width: 901px) { - .route-stage { - animation: route-stage-in 240ms var(--ease-out-soft) both; - } + @apply h-full min-h-0 overflow-auto; } .mobile-sidebar-mask { diff --git a/webapp/src/styles/tokens.css b/webapp/src/styles/tokens.css index 138a646..5d30614 100644 --- a/webapp/src/styles/tokens.css +++ b/webapp/src/styles/tokens.css @@ -1,21 +1,28 @@ :root { - --bg-accent: #e7edf8; - --panel: #f9fbff; - --panel-soft: #f2f6fd; - --panel-muted: #e8eff9; - --line: rgba(128, 152, 192, 0.32); - --line-soft: rgba(143, 167, 206, 0.18); + --bg-accent: #eef3fa; + --panel: #ffffff; + --panel-soft: #f6f8fc; + --panel-muted: #edf2f8; + --panel-subtle: #f8fafc; + --line: rgba(113, 132, 163, 0.28); + --line-soft: rgba(113, 132, 163, 0.16); --text: #0b1730; - --muted: #60708b; - --muted-strong: #334765; + --muted: #5f6f85; + --muted-strong: #2f4058; --primary: #2563eb; --primary-hover: #1d4ed8; --primary-strong: #0f3f98; --danger: #d92d57; + --success: #0f766e; + --warning: #b45309; --overlay-strong: rgba(15, 23, 42, 0.56); - --shadow-sm: 0 10px 22px rgba(13, 31, 68, 0.045); - --shadow-md: 0 22px 48px rgba(13, 31, 68, 0.08); - --shadow-lg: 0 28px 76px rgba(13, 31, 68, 0.11); + --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05); + --shadow-md: 0 8px 24px rgba(15, 23, 42, 0.08); + --shadow-lg: 0 14px 38px rgba(15, 23, 42, 0.10); + --radius-sm: 8px; + --radius-md: 10px; + --radius-lg: 14px; + --radius-xl: 18px; --ease-out-strong: cubic-bezier(0.22, 1, 0.36, 1); --ease-out-soft: cubic-bezier(0.24, 0.8, 0.32, 1); --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1); @@ -26,21 +33,24 @@ } :root[data-theme='dark'] { - --bg-accent: #06111d; - --panel: #0d192b; - --panel-soft: #112136; - --panel-muted: #0a1626; - --line: rgba(108, 141, 190, 0.28); - --line-soft: rgba(120, 152, 198, 0.16); - --text: #edf4ff; - --muted: #8fa6c6; - --muted-strong: #c3d5ef; - --primary: #84b6ff; - --primary-hover: #a6ccff; - --primary-strong: #f3f8ff; - --danger: #ff8ba8; - --overlay-strong: rgba(2, 8, 20, 0.84); - --shadow-sm: 0 14px 28px rgba(1, 7, 18, 0.24); - --shadow-md: 0 24px 52px rgba(1, 7, 18, 0.36); - --shadow-lg: 0 34px 88px rgba(1, 7, 18, 0.46); + --bg-accent: #0b1020; + --panel: #111827; + --panel-soft: #0f172a; + --panel-muted: #0b1324; + --panel-subtle: #151e2e; + --line: rgba(148, 163, 184, 0.20); + --line-soft: rgba(148, 163, 184, 0.12); + --text: #e5edf8; + --muted: #9aa8bb; + --muted-strong: #c7d2e2; + --primary: #8bb8ff; + --primary-hover: #a9ccff; + --primary-strong: #dceaff; + --danger: #fb7185; + --success: #5eead4; + --warning: #fbbf24; + --overlay-strong: rgba(2, 6, 23, 0.74); + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.26); + --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.30); + --shadow-lg: 0 14px 38px rgba(0, 0, 0, 0.34); } diff --git a/webapp/src/styles/vault.css b/webapp/src/styles/vault.css index 88972bc..f63fb3d 100644 --- a/webapp/src/styles/vault.css +++ b/webapp/src/styles/vault.css @@ -1,42 +1,27 @@ .vault-grid { - display: grid; + @apply grid h-full min-h-0 gap-3 p-0.5; grid-template-columns: 240px minmax(420px, 46%) minmax(575px, 1fr); - gap: 12px; - height: 100%; - min-height: 0; - padding: 2px; } .sidebar, .list-panel, .card { - background: var(--panel); - border: 1px solid var(--line); - border-radius: 18px; - box-shadow: var(--shadow-sm); + @apply border bg-panel shadow-soft; + border-color: var(--line); + border-radius: 16px; } .sidebar { - padding: 0; - overflow: auto; - border: none; - box-shadow: none; - background: transparent; + @apply overflow-auto border-0 bg-transparent p-0 shadow-none; } .sidebar-block { - border: 1px solid var(--line); - border-radius: 19px; - padding: 12px; - margin-bottom: 8px; - background: var(--panel); + @apply mb-2 rounded-2xl border bg-panel p-3; + border-color: var(--line); } .sidebar-title { - font-size: 13px; - font-weight: 700; - color: #344054; - margin-bottom: 8px; + @apply mb-2 text-[13px] font-bold text-slate-700; } .sidebar-title-row { @@ -51,21 +36,11 @@ } .folder-title-actions { - display: inline-flex; - align-items: center; - gap: 8px; + @apply inline-flex items-center gap-2; } .folder-add-btn { - border: none; - background: transparent; - color: #334155; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - padding: 0; - line-height: 1; + @apply inline-flex cursor-pointer items-center justify-center border-0 bg-transparent p-0 leading-none text-slate-700; } .folder-add-btn:hover { @@ -73,21 +48,10 @@ } .search-input { - width: 100%; - height: 48px; - border: 1px solid rgba(74, 103, 150, 0.42); - border-radius: 14px; - padding: 10px 14px; - font-size: 16px; - outline: none; - color: var(--text); + @apply h-12 w-full rounded-xl border px-3.5 py-2.5 text-base text-ink outline-none transition; background: var(--panel); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9); - transition: - border-color var(--dur-fast) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - background-color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft); + border-color: rgba(74, 103, 150, 0.34); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82); } .search-input-wrap { @@ -99,8 +63,7 @@ .search-input:focus { border-color: rgba(43, 102, 217, 0.6); background-color: #fbfdff; - box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.11), 0 10px 20px rgba(37, 99, 235, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.95); - transform: translateY(-1px); + box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.10), inset 0 1px 0 rgba(255, 255, 255, 0.95); } .search-input-wrap .search-input { @@ -137,27 +100,11 @@ } .tree-btn { - width: 100%; - min-width: 0; - border: none; - background: transparent; - text-align: left; - border-radius: 8px; - padding: 8px 10px; - margin-bottom: 4px; - cursor: pointer; - display: flex; - align-items: center; - gap: 8px; - transition: - background-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft); + @apply mb-1 flex w-full min-w-0 cursor-pointer items-center gap-2 rounded-lg border-0 bg-transparent px-2.5 py-2 text-left transition; } .tree-btn:hover { - transform: translateX(2px); + background: rgba(37, 99, 235, 0.05); } .tree-btn.active { @@ -218,11 +165,7 @@ } .list-col { - display: flex; - flex-direction: column; - min-width: 0; - min-height: 0; - max-width: 540px; + @apply flex min-h-0 min-w-0 max-w-[540px] flex-col; } .toolbar { @@ -241,10 +184,7 @@ } .list-head { - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 8px; + @apply mb-2 flex items-center gap-2.5; } .list-head .search-input-wrap { @@ -343,32 +283,13 @@ } .list-panel { - overflow: auto; - min-height: 0; - padding: 8px; + @apply min-h-0 overflow-auto p-2; } .list-item { - width: 100%; - background: rgba(249, 251, 255, 0.88); - border: 1px solid var(--line); - border-radius: 14px; - padding: 10px 12px; - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 8px; - min-height: 66px; - box-sizing: border-box; - position: relative; - cursor: pointer; - overflow: hidden; - transform-origin: 50% 50%; - transition: - background-color var(--dur-fast) var(--ease-smooth), - border-color var(--dur-fast) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - transform var(--dur-fast) var(--ease-out-soft); + @apply relative mb-2 flex min-h-[66px] w-full cursor-pointer items-center gap-2.5 overflow-hidden rounded-xl border px-3 py-2.5 transition; + background: rgba(249, 251, 255, 0.9); + border-color: var(--line); contain: paint; } @@ -395,8 +316,7 @@ .list-item:hover { background: #fcfdff; border-color: rgba(148, 163, 184, 0.26); - box-shadow: 0 16px 28px rgba(15, 23, 42, 0.06); - transform: translate3d(0, -2px, 0) scale(1.008); + box-shadow: 0 10px 22px rgba(15, 23, 42, 0.05); } .list-item:hover::before { @@ -407,8 +327,7 @@ .list-item.active { background: rgba(37, 99, 235, 0.1); border-color: rgba(43, 102, 217, 0.26); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42), 0 14px 24px rgba(43, 102, 217, 0.08); - transform: translate3d(0, -1px, 0) scale(1.004); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42); } .list-item.active::before { @@ -564,8 +483,7 @@ } .card { - padding: 16px 18px; - margin-bottom: 10px; + @apply mb-2.5 px-[18px] py-4; } .detail-col > .card { diff --git a/webapp/src/tailwind.css b/webapp/src/tailwind.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/webapp/src/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities;