From 5521a7f3834ac3e0aad8845adc32c55ace3e2edd Mon Sep 17 00:00:00 2001 From: dekkerglen Date: Tue, 1 Oct 2024 19:14:17 -0400 Subject: [PATCH 01/66] update nav --- package-lock.json | 895 +++++++- package.json | 8 +- postcss.config.js | 6 + public/css/dark.css | 8 +- public/css/default.css | 6 +- public/css/stylesheet.css | 2674 ++++++++++++++-------- routes/users_routes.js | 21 +- serverjs/render.js | 5 +- src/components/BlogContextMenu.tsx | 2 +- src/components/CardSearchBar.tsx | 18 +- src/components/Comment.tsx | 2 +- src/components/CommentContextMenu.tsx | 2 +- src/components/CommentEntry.tsx | 2 +- src/components/CommentsSection.tsx | 2 +- src/components/LinkButton.js | 24 - src/components/LoginModal.js | 56 - src/components/LoginModal.tsx | 42 + src/components/NotificationsNav.js | 68 - src/components/RenderToRoot.tsx | 1 + src/components/SampleHandModal.js | 2 +- src/components/WithModal.tsx | 2 +- src/components/base/Badge.tsx | 36 + src/components/base/Button.tsx | 54 + src/components/base/Card.tsx | 31 + src/components/base/Collapse.tsx | 16 + src/components/base/Container.tsx | 30 + src/components/base/Flexbox.tsx | 69 + src/components/base/Input.tsx | 40 + src/components/base/LinkButton.tsx | 26 + src/components/base/LinkDiv.tsx | 0 src/components/base/Modal.tsx | 109 + src/components/base/NavButton.tsx | 25 + src/components/base/NavLink.tsx | 25 + src/components/base/NavMenu.tsx | 84 + src/components/base/ResponsiveDiv.tsx | 45 + src/components/nav/Navbar.tsx | 164 ++ src/components/{ => nav}/Notification.js | 0 src/components/nav/NotificationsNav.tsx | 70 + src/contexts/ThemeContext.ts | 5 - src/css/stylesheet.css | 63 + src/datatypes/Notification.ts | 11 + src/datatypes/User.ts | 3 + src/layouts/Footer.tsx | 79 +- src/layouts/MainLayout.tsx | 178 +- src/pages/NotificationsPage.js | 2 +- tailwind.config.js | 119 + views/main.pug | 5 +- yarn.lock | 507 +++- 48 files changed, 4316 insertions(+), 1326 deletions(-) create mode 100644 postcss.config.js delete mode 100644 src/components/LinkButton.js delete mode 100644 src/components/LoginModal.js create mode 100644 src/components/LoginModal.tsx delete mode 100644 src/components/NotificationsNav.js create mode 100644 src/components/base/Badge.tsx create mode 100644 src/components/base/Button.tsx create mode 100644 src/components/base/Card.tsx create mode 100644 src/components/base/Collapse.tsx create mode 100644 src/components/base/Container.tsx create mode 100644 src/components/base/Flexbox.tsx create mode 100644 src/components/base/Input.tsx create mode 100644 src/components/base/LinkButton.tsx create mode 100644 src/components/base/LinkDiv.tsx create mode 100644 src/components/base/Modal.tsx create mode 100644 src/components/base/NavButton.tsx create mode 100644 src/components/base/NavLink.tsx create mode 100644 src/components/base/NavMenu.tsx create mode 100644 src/components/base/ResponsiveDiv.tsx create mode 100644 src/components/nav/Navbar.tsx rename src/components/{ => nav}/Notification.js (100%) create mode 100644 src/components/nav/NotificationsNav.tsx delete mode 100644 src/contexts/ThemeContext.ts create mode 100644 src/css/stylesheet.css create mode 100644 src/datatypes/Notification.ts create mode 100644 tailwind.config.js diff --git a/package-lock.json b/package-lock.json index 306d5d41b..d8b5ed854 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@aws-sdk/client-cloudwatch-logs": "^3.433.0", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", + "@headlessui/react": "^2.1.8", "@primer/octicons-react": "^19.11.0", "@tensorflow/tfjs-node": "^4.12.0", "assert": "^2.0.0", @@ -128,13 +129,16 @@ "@types/react-timeago": "^4.1.7", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", + "autoprefixer": "^10.4.20", "babel-loader": "^9.1.3", "css-loader": "^7.1.2", "eslint": "^9.11.1", "globals": "^15.9.0", "nodemon": "^3.1.4", + "postcss": "^8.4.47", "raw-loader": "^4.0.2", "style-loader": "^4.0.0", + "tailwindcss": "^3.4.13", "ts-loader": "^9.5.1", "typescript": "^5.5.4", "typescript-eslint": "^8.0.0", @@ -146,6 +150,18 @@ "node": ">=20.0.0 <21.0.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -3012,6 +3028,54 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.24", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.24.tgz", + "integrity": "sha512-2ly0pCkZIGEQUq5H8bBK0XJmc1xIK/RM3tvVzY3GBER7IOD1UgmC2Y2tjj4AuS+TC+vTE1KJv2053290jua0Sw==", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" + }, "node_modules/@hapi/boom": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", @@ -3027,6 +3091,24 @@ "integrity": "sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==", "license": "BSD-3-Clause" }, + "node_modules/@headlessui/react": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.8.tgz", + "integrity": "sha512-uajqVkAcVG/wHwG9Fh5PFMcFpf2VxM4vNRNKxRjuK009kePVur8LkuuygHfIE+2uZ7z7GnlTtYsyUe6glPpTLg==", + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.17.1", + "@react-aria/interactions": "^3.21.3", + "@tanstack/react-virtual": "^3.8.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18", + "react-dom": "^18" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -3074,6 +3156,102 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -3617,6 +3795,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -3639,6 +3827,64 @@ "react": ">=16.3" } }, + "node_modules/@react-aria/focus": { + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.18.2.tgz", + "integrity": "sha512-Jc/IY+StjA3uqN73o6txKQ527RFU7gnG5crEl5Xy3V+gbYp2O5L3ezAo/E0Ipi2cyMbG6T5Iit1IDs7hcGu8aw==", + "dependencies": { + "@react-aria/interactions": "^3.22.2", + "@react-aria/utils": "^3.25.2", + "@react-types/shared": "^3.24.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.22.2.tgz", + "integrity": "sha512-xE/77fRVSlqHp2sfkrMeNLrqf2amF/RyuAS6T5oDJemRSgYM3UoxTbWjucPhfnoW7r32pFPHHgz4lbdX8xqD/g==", + "dependencies": { + "@react-aria/ssr": "^3.9.5", + "@react-aria/utils": "^3.25.2", + "@react-types/shared": "^3.24.1", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.5.tgz", + "integrity": "sha512-xEwGKoysu+oXulibNUSkXf8itW0npHHTa6c4AyYeZIJyRoegeteYuFpZUBPtIDE8RfHdNsSmE1ssOkxRnwbkuQ==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.25.2.tgz", + "integrity": "sha512-GdIvG8GBJJZygB4L2QJP1Gabyn2mjFsha73I2wSe+o4DYeGWoJiMZRM06PyTIxLH4S7Sn7eVDtsSBfkc2VY/NA==", + "dependencies": { + "@react-aria/ssr": "^3.9.5", + "@react-stately/utils": "^3.10.3", + "@react-types/shared": "^3.24.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@react-mock/fetch": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@react-mock/fetch/-/fetch-0.3.0.tgz", @@ -3651,6 +3897,25 @@ "lodash": "^4.17.11" } }, + "node_modules/@react-stately/utils": { + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.3.tgz", + "integrity": "sha512-moClv7MlVSHpbYtQIkm0Cx+on8Pgt1XqtPx6fy9rQFb2DNc9u1G3AUVnqA17buOkH1vLxAtX4MedlxMWyRCYYA==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-types/shared": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.24.1.tgz", + "integrity": "sha512-AUQeGYEm/zDTN6zLzdXolDxz3Jk5dDL7f506F07U8tBwxNNI3WRdhU84G0/AaFikOZzDXhOZDr3MhQMzyE7Ydw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", @@ -4303,6 +4568,39 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "license": "MIT" }, + "node_modules/@swc/helpers": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz", + "integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz", + "integrity": "sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==", + "dependencies": { + "@tanstack/virtual-core": "3.10.8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.8.tgz", + "integrity": "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tensorflow/tfjs": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-4.21.0.tgz", @@ -5549,6 +5847,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -5596,6 +5900,12 @@ "node": ">= 6" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5731,6 +6041,43 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "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" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -6442,6 +6789,15 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/can-use-dom": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz", @@ -6791,6 +7147,14 @@ "node": ">=0.10.0" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -7554,6 +7918,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, "node_modules/discontinuous-range": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", @@ -7574,6 +7944,12 @@ "node": ">=4" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -7710,6 +8086,12 @@ "aws-sdk": "^2.100.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -9091,6 +9473,34 @@ "node": ">=0.10.0" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/forever-monitor": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/forever-monitor/-/forever-monitor-1.7.2.tgz", @@ -9274,6 +9684,19 @@ "node": ">= 0.6" } }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -10732,6 +11155,21 @@ "is-stream": "^1.0.1" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -10761,6 +11199,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/jmespath": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", @@ -11067,6 +11514,21 @@ "license": "MIT", "optional": true }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -12842,6 +13304,17 @@ "mustache": "bin/mustache" } }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nan": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", @@ -13076,10 +13549,19 @@ "node": ">=0.10.0" } }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "license": "MIT", "optional": true, "dependencies": { @@ -13122,6 +13604,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -13401,6 +13892,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/papaparse": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", @@ -13638,6 +14135,37 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-to-regexp": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", @@ -13733,6 +14261,15 @@ "node": ">=6" } }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -13837,7 +14374,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", @@ -13847,6 +14383,89 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "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.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/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" + } + ], + "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-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/postcss-modules-extract-imports": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", @@ -13910,6 +14529,31 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/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" + } + ], + "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.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", @@ -14817,6 +15461,24 @@ "react-dom": ">=16.8.0" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -16355,6 +17017,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -16381,6 +17058,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -16436,6 +17126,90 @@ "inline-style-parser": "0.2.4" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -16469,6 +17243,60 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, + "node_modules/tailwindcss": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", + "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -16563,6 +17391,27 @@ "dev": true, "license": "MIT" }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/thingies": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", @@ -16739,6 +17588,12 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, "node_modules/ts-loader": { "version": "9.5.1", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", @@ -18009,6 +18864,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -18096,6 +18969,18 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 29d3048b7..017bd1911 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "node": ">=20.0.0 <21.0.0" }, "scripts": { - "build": "yarn run nearley && yarn run sass && tsc -b && yarn run webpack", + "build": "yarn run nearley && yarn run sass && tsc -b && yarn run webpack && && npx tailwindcss -i ./src/css/stylesheet.css -o ./public/css/stylesheet.css", "lint": "yarn run prettier-base --check && yarn run eslint --max-warnings 0", "bash": "bash", "beautify": "yarn run prettier", @@ -25,7 +25,7 @@ "test:watch": "yarn run webpack --watch & yarn run test-loud --watch --verbose false", "nearley": "sh nearley/helper.sh", "ci-build": "yarn run nearley && yarn run sass && tsc -b && NODE_OPTIONS=--max_old_space_size=16384 node_modules/.bin/webpack --mode production --config webpack.prod.mjs", - "devstart": "yarn run nearley && yarn run sass --watch & yarn run nodemon & yarn run webpack-dev-server & yarn run webpack --watch", + "devstart": "yarn run nearley && yarn run sass --watch & yarn run nodemon & yarn run webpack-dev-server & yarn run webpack --watch & npx tailwindcss -i ./src/css/stylesheet.css -o ./public/css/stylesheet.css --watch", "setup": "yarn run nearley && yarn run webpack --progress && node --max-old-space-size=8192 force_update.js", "cards": "node --max-old-space-size=6000 force_update.js", "download-model": "node --max-old-space-size=4096 jobs/download_model.js", @@ -50,6 +50,7 @@ "@aws-sdk/client-cloudwatch-logs": "^3.433.0", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", + "@headlessui/react": "^2.1.8", "@primer/octicons-react": "^19.11.0", "@tensorflow/tfjs-node": "^4.12.0", "assert": "^2.0.0", @@ -166,13 +167,16 @@ "@types/react-timeago": "^4.1.7", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", + "autoprefixer": "^10.4.20", "babel-loader": "^9.1.3", "css-loader": "^7.1.2", "eslint": "^9.11.1", "globals": "^15.9.0", "nodemon": "^3.1.4", + "postcss": "^8.4.47", "raw-loader": "^4.0.2", "style-loader": "^4.0.0", + "tailwindcss": "^3.4.13", "ts-loader": "^9.5.1", "typescript": "^5.5.4", "typescript-eslint": "^8.0.0", diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 000000000..33ad091d2 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/public/css/dark.css b/public/css/dark.css index 3272827a9..d368d0f9f 100644 --- a/public/css/dark.css +++ b/public/css/dark.css @@ -37,11 +37,11 @@ --preview-article: rgba(116, 198, 135, 0.5); --preview-podcast:rgba(0, 0, 0, 0.6); --preview-video: rgba(254, 66, 66, 0.5); - --nav-link: rgba(255,255,255,.5); + --text-secondary: rgba(255,255,255,.5); --button-green: #087715; --button-red: #bc1525; - --secondary-nav-link:rgba(255,255,255,.5); - --secondary-nav-link-hover:rgba(255,255,255,.8); + --secondary-text-secondary:rgba(255,255,255,.5); + --secondary-text-secondary-hover:rgba(255,255,255,.8); --dropdown-hover: #b6b8bb; --code-color:#fb7edc; --code-bg: rgb(43, 43, 43); @@ -54,4 +54,4 @@ :not(.alert) > .btn-close { filter: invert(1) grayscale(100%) brightness(200%); -} \ No newline at end of file +} diff --git a/public/css/default.css b/public/css/default.css index c8e85191f..218eb3dab 100644 --- a/public/css/default.css +++ b/public/css/default.css @@ -37,11 +37,11 @@ --preview-article: rgba(116, 198, 135, 0.5); --preview-podcast:rgba(0, 0, 0, 0.6); --preview-video: rgba(254, 66, 66, 0.5); - --nav-link: rgba(255,255,255,.5); + --text-secondary: rgba(255,255,255,.5); --button-green: #28a745; --button-red: #dc3545; - --secondary-nav-link:rgba(0,0,0,.5); - --secondary-nav-link-hover:rgba(0,0,0,.7); + --secondary-text-secondary:rgba(0,0,0,.5); + --secondary-text-secondary-hover:rgba(0,0,0,.7); --dropdown-hover: #16181b; --code-color:#d71d72; --code-bg: rgb(245, 245, 245); diff --git a/public/css/stylesheet.css b/public/css/stylesheet.css index e43ed9ebd..e69ea0458 100644 --- a/public/css/stylesheet.css +++ b/public/css/stylesheet.css @@ -1,1378 +1,2208 @@ +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +/* +! tailwindcss v3.4.13 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + /* 3 */ + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ body { - background-color: var(--bg); - color: var(--text); + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +:root { + --bg: 255 255 255; + --bg-active: 233 233 233; + --bg-secondary: 52 58 64; + --text-secondary: 128 128 128; + --text-secondary-active: 255 255 255; + --border: 212 212 212; + --text: 33 37 41; + --link: 0 123 255; + --link-active: 0 86 179; + --button-text: 255 255 255; + --button-text-secondary: 33 37 41; + --button-success: 8 119 21; + --button-success-active : 0 75 13; + --button-danger: 188 21 37; + --button-danger-active: 139 0 0; + --button-primary: 0 123 255; + --button-primary-active: 0 86 179; + --button-secondary: 108 117 125; + --button-secondary-active: 74 80 87; + --focus-ring: 3 102 214; +} + +:root[class~="dark"] { + --bg: 52 58 64; + --bg-active: 74 80 87; + --bg-secondary: 36 41 46; + --text-secondary: 128 128 128; + --text-secondary-active: 255 255 255; + --border: 37 42 46; + --text: 255 255 255; + --link: 50 166 255; + --link-active: 0 86 179; + --button-text: 255 255 255; + --button-text-secondary: 33 37 41; + --button-success: 8 119 21; + --button-success-active: 7 101 18; + --button-danger: 220 53 69; + --button-danger-active: 139 0 0; + --button-primary: 50 166 255; + --button-primary-active: 0 86 179; + --button-secondary: 108 117 125; + --button-secondary-active: 74 80 87; + --focus-ring: 3 102 214; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } } -.card { - background-color: var(--bg); +.pointer-events-none { + pointer-events: none; } -.cubenav { - border-right: 1px solid var(--border-light); - border-left: 1px solid var(--border-light); +.visible { + visibility: visible; } -.usercontrols { - background-image: linear-gradient(var(--bg), var(--bg-dark)); - border-right: 1px solid var(--border-light); - border-left: 1px solid var(--border-light); - border-bottom: 1px solid var(--border-light); +.\!collapse { + visibility: collapse !important; } -.nav-pills .nav-link.active { - background-color: var(--success); +.collapse { + visibility: collapse; } -.nav-pills .nav-link:not(.active) { - color: var(--success); +.static { + position: static; } -.nav-link:not(.hover) { - color: var(--success); +.\!fixed { + position: fixed !important; } -.nav-link.hover { - color: var(--success); +.fixed { + position: fixed; } +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.inset-0 { + inset: 0px; +} -.col-1-2 { - flex: 0 0 10%; - max-width: 10%; +.right-0 { + right: 0px; } -@media (max-width: 575.98px) { - /* only on xs */ - .nav-link { - padding: 0.375rem 0.5rem; - font-size: 11pt; - } +.z-10 { + z-index: 10; } -.pagination > li > a, -.pagination > li > button { - background-color: var(--bg); - color: var(--text); +.z-30 { + z-index: 30; } -.pagination > li > a:focus, -.pagination > li > a:hover, -.pagination > li > button:focus, -.pagination > li > button:hover, -.pagination > li > span:focus, -.pagination > li > span:hover { - background-color: var(--bg-hover); - color: var(--text); +.col-auto { + grid-column: auto; } -.pagination > .active > a, -.pagination > .active > button { - color: #fff !important; - background-color: var(--success) !important; - border: solid 1px var(--success) !important; +.float-start { + float: inline-start; } -.pagination > .active > a:hover, -.pagination > .active > button:hover { - background-color: var(--success) !important; - border: solid 1px var(--success); +.float-end { + float: inline-end; } -.editarea { - overflow: auto; - max-height: 200px; +.m-0 { + margin: 0px; } -.list-group.list-outline { - border: 1px solid var(--border-dark); - border-radius: 5px; - margin-bottom: 5px; - overflow: hidden; +.m-1 { + margin: 0.25rem; } -.list-group, -.list-group-item { - display: block; - border: none; - background-color: var(--bg); - color: var(--text); +.m-2 { + margin: 0.5rem; } -.list-group-item.list-group-heading { - background-color: var(--header) !important; - text-align: center; - color:var(--text); - text-decoration: none; - padding: 5px 10px; - font-size: 9pt; - font-weight: 600; - border-bottom: 1px solid var(--border-dark); - margin-bottom: 0px; +.-mx-4 { + margin-left: -1rem; + margin-right: -1rem; } -.list-group-item.list-group-heading.clickable:hover { - background-color: var(--bg) !important; - color:var(--text); - text-decoration: none; +.mx-0 { + margin-left: 0px; + margin-right: 0px; } -.card-list-item { - list-style-type: none; - transition: 0.25s; - border-radius: 0 !important; - cursor: pointer; - font-size: 8.5pt; +.mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; +} - color:var(--card-text); - padding: 3px 4px; - text-decoration: none; +.mx-2 { + margin-left: 0.5rem; + margin-right: 0.5rem; } -.card-list-item, -.card-list-item .name { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; +.mx-3 { + margin-left: 0.75rem; + margin-right: 0.75rem; } - -@media (max-width: 1199.98px) { - /* xs-lg */ - .card-list-item { - font-size: 8.5pt; - } -} - -@media (max-width: 991.98px) { - /* xs-md */ - .card-list-heading { - font-size: 11pt; - } - .list-group-item.list-group-heading { - font-size: 8.5pt; - } - .card-list-item { - font-size: 8pt; - padding: 2px 4px; - } - .table-col { - flex: 0 0 calc(100% / 9); - width: calc(100% / 9); - } + +.mx-4 { + margin-left: 1rem; + margin-right: 1rem; } -@media (max-width: 767.98px) { - /* xs and sm only */ - .table-col { - flex: 0 0 calc(100% / 4); - width: calc(100% / 4); - } +.mx-auto { + margin-left: auto; + margin-right: auto; } -@media (max-width: 459.98px) { - /* custom breakpoint */ - .table-col { - flex: 0 0 calc(100% / 3); - width: calc(100% / 3); - } +.my-0 { + margin-top: 0px; + margin-bottom: 0px; } -@media (max-width: 991.98px) { - .compressed.table-view.row { - transform: scale(0.5) translate(-50%, -50%); - padding: 0; - margin: 0 -15px; - width: 200vw; - overflow: visible; - } - .compressed .card-list-heading { - font-size: 10pt !important; - } +.my-1 { + margin-top: 0.25rem; + margin-bottom: 0.25rem; } -@media (max-width: 649.98px) { - .compressed .table-col { - margin-top: 0.375rem !important; - } - .compressed .card-list-heading { - margin-bottom: 0.375rem !important; - overflow: hidden; - text-overflow: ellipsis; - } - .compressed .list-group-heading { - padding: 2px 4px; - } - .compressed .card-list-item { - padding: 0 2px; - text-overflow: clip; - } +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; } -.card-list-item button.close { - font-size: 1rem; +.my-3 { + margin-top: 0.75rem; + margin-bottom: 0.75rem; } -.card-list-item button.close span { - vertical-align: top; - line-height: 0.9; +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; } -.card-list-item:hover { - color:var(--text); - text-decoration: none; +.mb-0 { + margin-bottom: 0px; } -.card-list-item:after { - content: ''; - display: block; - height: 100%; - width: 100%; - opacity: 0; - background: var(--hover); - top: 0; - left: 0; - position: absolute; - pointer-events: none; +.mb-1 { + margin-bottom: 0.25rem; } -.tag-color.card-list-item:after { - background: var(--hover-inverse); +.mb-2 { + margin-bottom: 0.5rem; } -.tag-color-row { - background-color: var(--bg); - color: var(--text); +.mb-3 { + margin-bottom: 0.75rem; } -.card-list-item:hover:after { - opacity: 1; +.mb-4 { + margin-bottom: 1rem; } -.cmc-group:not(:first-of-type) { - border-top: 1px solid var(--border-darker); +.mb-5 { + margin-bottom: 1.25rem; } -.white { - border-color: var(--card-white); - background-color: var(--card-white); +.mb-6 { + margin-bottom: 1.5rem; } -.blue { - border-color: var(--card-blue); - background-color: var(--card-blue); + +.me-0 { + margin-inline-end: 0px; } -.black { - border-color: var(--card-black); - background-color: var(--card-black); + +.me-1 { + margin-inline-end: 0.25rem; } -.red { - border-color: var(--card-red); - background-color: var(--card-red); + +.me-2 { + margin-inline-end: 0.5rem; } -.green { - border-color: var(--card-green); - background-color: var(--card-green); + +.me-3 { + margin-inline-end: 0.75rem; +} + +.me-auto { + margin-inline-end: auto; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.ml-auto { + margin-left: auto; +} + +.ms-1 { + margin-inline-start: 0.25rem; +} + +.ms-2 { + margin-inline-start: 0.5rem; +} + +.ms-auto { + margin-inline-start: auto; +} + +.mt-0 { + margin-top: 0px; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-6 { + margin-top: 1.5rem; +} + +.mt-auto { + margin-top: auto; +} + +.block { + display: block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; } -.multi { - border-color: var(--card-multi); - background-color: var(--card-multi); + +.inline-flex { + display: inline-flex; } -.colorless { - border-color: var(--card-colorless); - background-color: var(--card-colorless); + +.table { + display: table; } -.lands { - border-color: var(--card-lands); - background-color: var(--card-lands); + +.hidden { + display: none; } -.dropdown-menu a:focus { - background-color: var(--success); - color: var(--bg); +.h-10 { + height: 2.5rem; } -.dropdown-no-green a:focus { - background-color: transparent; - color: var(--link); +.h-full { + height: 100%; } -#autocardPopup { - position: absolute; - pointer-events: none; - user-select: none; - -moz-user-select: none; - -webkit-user-drag: none; - -webkit-user-select: none; - -ms-user-select: none; - border-radius: 10px; - overflow: hidden; +.max-h-0 { + max-height: 0px; } -#autocardPopup img { - width: 15rem; +.max-h-96 { + max-height: 24rem; } -#autocardPopup { - width: 15rem; +.max-h-fit { + max-height: fit-content; } -#autocardPopup.double-width { - width: 30rem; +.max-h-screen { + max-height: 100vh; } -.autocard-background { - background-color: var(--bg-dark); - background-clip: content-box; +.min-h-full { + min-height: 100%; } -@media (max-width: 767.98px) { - #autocardPopup { - display: none !important; - } +.min-h-screen { + min-height: 100vh; } -/* make autocard text more obvious in comments and descriptions */ -.card-text .autocard { - font-weight: bolder; - opacity: 0.9; /* hack to lighten the color so bold isn't quite so strong */ +.w-64 { + width: 16rem; } -.form-check { - border: none; +.w-96 { + width: 24rem; } -.foilOverlay { - position: absolute; - pointer-events: none; - width: 100%; - height: 100%; - mix-blend-mode: multiply; +.w-auto { + width: auto; } -#syntax-accordion .card, -#syntax-accordion { - width: 100%; +.w-full { + width: 100%; } -#syntax-accordion .collapse, -#syntax-accordion .collapsing { - padding: 0.75rem 1.25rem; + +.max-w-lg { + max-width: 32rem; } -code { - color: var(--code-color) + +.max-w-md { + max-width: 28rem; } -#syntax-accordion .card-header { - cursor: pointer; - transition: 0.25s; + +.max-w-sm { + max-width: 24rem; } -#syntax-accordion .card-header:hover { - background: var(--hover); + +.max-w-xl { + max-width: 36rem; } -#syntax-accordion .card-header button { - color: var(--text); + +.flex-grow { + flex-grow: 1; } -div.ReactTags__tags { - margin-bottom: -0.3rem; +.grow { + flex-grow: 1; } -div.ReactTags__tagInput { - flex: 1 1 5rem; - margin-bottom: 0.3rem; +.translate-y-0 { + --tw-translate-y: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } -div.ReactTags__tagInput input.ReactTags__tagInputField, -div.ReactTags__tagInput input.ReactTags__tagInputField:focus { - width: 100%; - height: calc(1.5em + 0.5rem + 2px); - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; - font-weight: 400; - color: var(--text); - background-color: var(--bg); - background-clip: padding-box; - border: 1px solid var(--border); - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +.translate-y-4 { + --tw-translate-y: 1rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } -div.ReactTags__selected { - display: flex; - flex-wrap: wrap; - margin: 0; +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } -/* Styles for selected tags */ -div.ReactTags__selected span.ReactTags__tag { - border: 1px solid var(--border); - background-color: var(--bg-hover); - color: var(--text); - display: block; - padding: 0.25rem 0.5rem; - margin-right: 0.3rem; - margin-bottom: 0.3rem; - border-radius: 2px; - font-size: 0.875rem; +.cursor-not-allowed { + cursor: not-allowed; } -div.ReactTags__selected a.ReactTags__remove { - color: var(--border-dark); - margin-left: 5px; - cursor: pointer; + +.cursor-pointer { + cursor: pointer; } -/* Styles for suggestions */ -div.ReactTags__suggestions { - position: absolute; +.select-none { + -webkit-user-select: none; + user-select: none; } -div.ReactTags__suggestions ul { - list-style-type: none; - box-shadow: 0.05em 0.01em 0.5em var(--shadow); - background: var(--bg); - width: 200px; + +.list-none { + list-style-type: none; } -div.ReactTags__suggestions li { - border-bottom: 1px solid var(--border); - padding: 5px 10px; - margin: 0; + +.flex-row { + flex-direction: row; } -div.ReactTags__suggestions li mark { - background: none; - font-weight: 600; + +.flex-row-reverse { + flex-direction: row-reverse; } -div.ReactTags__suggestions ul li.ReactTags__activeSuggestion { - background: var(--hover); - cursor: pointer; + +.flex-col { + flex-direction: column; } -/* Fix z-index on sortables. */ -.sortable-item { - z-index: 10000 !important; +.flex-col-reverse { + flex-direction: column-reverse; } -.clickable { - cursor: pointer; +.flex-wrap { + flex-wrap: wrap; } -/* Give cards sharp borders. */ -div.card { - box-shadow: 2px 2px 6px 0 var(--shadow-light); - border-radius: 0px; +.flex-wrap-reverse { + flex-wrap: wrap-reverse; } -body.busy-cursor { - cursor: progress; +.flex-nowrap { + flex-wrap: nowrap; } -.centered { - display: flex; - justify-content: center; - align-items: center; +.content-center { + align-content: center; } -.markdown { - padding-left: 1rem; - padding-right: 1rem; +.content-start { + align-content: flex-start; } -.markdown .centered-markdown { - width: 100%; - text-align: center; +.content-end { + align-content: flex-end; } -.markdown .centered-markdown > .card-image, -.markdown .centered-markdown :not(.cardRow) > .card-image { - margin: auto; +.content-between { + align-content: space-between; } -.markdown .centered-markdown .cardRow { - justify-content: center; +.content-around { + align-content: space-around; } -.markdown pre { - background-color: var(--code-bg) !important; +.content-stretch { + align-content: stretch; } -.markdown li > input[type="checkbox"] { - margin-right: 0.2em; +.items-start { + align-items: flex-start; } -.markdown .quote { - background-color: var(--bg-dark); - margin-bottom: 1rem; +.items-end { + align-items: flex-end; } -.markdown .quote p:last-child { - margin-bottom: 0; +.items-center { + align-items: center; } -.markdown hr { - border-color: var(--border); +.items-baseline { + align-items: baseline; } -.markdown .markdown-image { - max-width: 100%; +.items-stretch { + align-items: stretch; } -.markdown .heading-link { - float: left; - margin-left: -1.5rem; - padding-right: 0.5rem; - vertical-align: middle; - text-decoration: none; - color: currentColor; +.justify-start { + justify-content: flex-start; } -.markdown .heading-link .link-icon { - vertical-align: middle !important; - visibility: hidden; +.justify-end { + justify-content: flex-end; } -.markdown h1:hover .heading-link .link-icon, -.markdown h2:hover .heading-link .link-icon, -.markdown h3:hover .heading-link .link-icon, -.markdown h4:hover .heading-link .link-icon, -.markdown h5:hover .heading-link .link-icon, -.markdown h6:hover .heading-link .link-icon { - visibility: visible; +.justify-center { + justify-content: center; } -@media (max-width: 1280px) { - .markdown .heading-link .link-icon { - visibility: visible; - } +.justify-between { + justify-content: space-between; } -.centered-max400 { - margin: auto; - max-width: 400px; +.justify-around { + justify-content: space-around; } -.cube-preview-image { - width: 100%; - height: auto; +.justify-evenly { + justify-content: space-evenly; } -.deck-preview { - padding: .83rem 1rem; - border-top: 1px solid var(--border-light); +.gap-0 { + gap: 0px; } -.deck-preview:first-child { - border-top: none; +.gap-1 { + gap: 0.25rem; } -.deck-preview:hover { - background: var(--bg-dark); - cursor: pointer; +.gap-10 { + gap: 2.5rem; } -.no-underline-hover:hover { - text-decoration: none; +.gap-11 { + gap: 2.75rem; } -/* Styles for Analysis */ +.gap-12 { + gap: 3rem; +} -.breakdown .percent { - display: inline-block; - font-size: 0.875rem; - color: var(--text-light); - margin-left: 0.5em; +.gap-14 { + gap: 3.5rem; } -.breakdown .percent::before { - content: '('; + +.gap-16 { + gap: 4rem; } -.breakdown .percent::after { - content: ')'; + +.gap-2 { + gap: 0.5rem; } -.cube-preview-card { - width: 100%; + +.gap-20 { + gap: 5rem; } -.cube-preview-card.hover { - background: var(--bg-dark); - cursor: pointer; - border: 1px solid #000000; + +.gap-24 { + gap: 6rem; } -.cube-preview-element { - height: 73%; + +.gap-28 { + gap: 7rem; } -.cube-preview-artist { - font-size: small; - position: absolute; - bottom: 0; - right: 2px; - color: #fff; - text-shadow: -1px 0 #000, 0 1px #000, 1px 0 #000, 0 -1px #000; +.gap-3 { + gap: 0.75rem; } -.content-preview-banner { - position: absolute; - bottom: 0; - left: 0; - color: var(--bg); - width: 100%; - margin-bottom: 0; - padding: 3px; +.gap-32 { + gap: 8rem; } -.article-preview-bg { - background: var(--preview-article); +.gap-36 { + gap: 9rem; } -.video-preview-bg { - background: var(--preview-video); +.gap-4 { + gap: 1rem; } -.podcast-preview-bg { - background: var(--preview-podcast); +.gap-40 { + gap: 10rem; } -.preview-footer-bg { - background: var(--bg-dark) +.gap-44 { + gap: 11rem; } -.preview-footer-bg-hover { - background: var(--hover) +.gap-48 { + gap: 12rem; } -.navbar-dark .navbar-nav .nav-link { - color: var(--nav-link); +.gap-5 { + gap: 1.25rem; } -.notification-icon { - background: var(--nav-link); - height: 18px; - margin: 0 0; +.gap-52 { + gap: 13rem; } -.nav-item:hover .notification-icon { - background: var(--hover); +.gap-56 { + gap: 14rem; } -.nav-item:focus .notification-icon { - background: var(--bg); + +.gap-6 { + gap: 1.5rem; } -.notification-scrollarea { - max-height: 60vh; - min-width: 40vh; - overflow-y: auto; +.gap-60 { + gap: 15rem; } -.user-notification { - border-top: 1px solid var(--border-light); +.gap-64 { + gap: 16rem; } -.user-notification:hover { - background: var(--hover); +.gap-7 { + gap: 1.75rem; } -.comment-highlighted { - background: var(--bg-dark); +.gap-72 { + gap: 18rem; } -.profile-thumbnail { - border-radius: 50%; - object-fit: cover; - width: 60px; - height: 60px; - float: left; - box-shadow: -1px 0 var(--text), 0 1px var(--text), 1px 0 var(--text), 0 -1px var(--text); +.gap-8 { + gap: 2rem; } -.content-preview-img { - object-fit: cover; - width:100%; +.gap-80 { + gap: 20rem; } -.table-view.row { - margin: 0 -2px; +.gap-9 { + gap: 2.25rem; } -.table-view .table-col { - padding: 0 2px; +.gap-96 { + gap: 24rem; } -@media (max-width: 991.98px) { - /* On screens where not all columns display at once, scroll cubes horizontally. */ - .table-view-container { - /* Size to viewport. Have to do this so that scroll displays to edge of screen. */ - overflow: hidden; - width: 100vw; - padding: 0 calc(50vw - 50%); - margin: -0.5rem calc(-50vw + 50%) 0rem; - } +.overflow-auto { + overflow: auto; +} - .table-view.row { - overflow: auto; - flex-wrap: nowrap; - padding: 0 calc(50vw - 50% - 2px); - margin: 0 calc(-50vw + 50%); - } +.overflow-hidden { + overflow: hidden; +} - .table-view.row:after { - content: ''; - min-width: calc(50vw - 50% - 2px); - height: 1px; - } +.overflow-y-auto { + overflow-y: auto; } .text-ellipsis { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + text-overflow: ellipsis; } -.card-border { - border-radius: 4.72% / 3.37%; +.rounded { + border-radius: 0.25rem; } -@media (min-width: 768px) { - @media (max-width: 991.98px) { - .col-md-1-5 { - flex: 0 0 12.5%; - max-width: 12.5%; - } +.rounded-full { + border-radius: 9999px; +} - .col-md-2-4 { - flex: 0 0 20%; - max-width: 20%; - } +.rounded-lg { + border-radius: 0.5rem; +} - .col-md-1-4285 { - flex: 0 0 14.285%; - max-width: 14.285%; - } - } +.rounded-md { + border-radius: 0.375rem; } -@media (min-width: 992px) { - @media (max-width: 1119.98px) { - .col-lg-1-5 { - flex: 0 0 12.5%; - max-width: 12.5%; - } +.rounded-br-none { + border-bottom-right-radius: 0px; +} - .col-lg-2-4 { - flex: 0 0 20%; - max-width: 20%; - } +.rounded-tr-none { + border-top-right-radius: 0px; +} - .col-lg-1-4285 { - flex: 0 0 14.285%; - max-width: 14.285%; - } - } +.border { + border-width: 1px; } -@media (min-width: 1200px) { - .col-xl-1-5 { - flex: 0 0 12.5%; - max-width: 12.5%; - } +.border-4 { + border-width: 4px; +} - .col-xl-2-4 { - flex: 0 0 20%; - max-width: 20%; - } +.border-y { + border-top-width: 1px; + border-bottom-width: 1px; +} - .col-xl-1-4285 { - flex: 0 0 14.285%; - max-width: 14.285%; - } +.border-b { + border-bottom-width: 1px; } -.input-group-prepend + .input-group-append > .color-check-button { - border-left: 0; +.border-t { + border-top-width: 1px; } -.color-check-button { - border-color: var(--border) !important; - background-color: var(--bg) !important; - padding: 0 0.5rem !important; +.border-border { + --tw-border-opacity: 1; + border-color: rgb(var(--border) / var(--tw-border-opacity)); } -.color-check-button:hover { - border-color: var(--border) !important; - background-color: var(--hover) !important; +.border-button-danger { + --tw-border-opacity: 1; + border-color: rgb(var(--button-danger) / var(--tw-border-opacity)); } -.color-check-button.active { - border-color: var(--border) !important; - background-color: var(--secondary) !important; +.border-button-primary { + --tw-border-opacity: 1; + border-color: rgb(var(--button-primary) / var(--tw-border-opacity)); } -img.mana-symbol { - height: 1.8rem; +.border-button-secondary { + --tw-border-opacity: 1; + border-color: rgb(var(--button-secondary) / var(--tw-border-opacity)); } -img.mana-symbol-sm { - height: 1.3rem; +.border-button-success { + --tw-border-opacity: 1; + border-color: rgb(var(--button-success) / var(--tw-border-opacity)); } -.square-left { - border-top-left-radius: 0; - border-bottom-left-radius: 0; +.border-button-success-active { + --tw-border-opacity: 1; + border-color: rgb(var(--button-success-active) / var(--tw-border-opacity)); } -.square-right { - border-top-right-radius: 0; - border-bottom-right-radius: 0; +.border-focus-ring { + --tw-border-opacity: 1; + border-color: rgb(var(--focus-ring) / var(--tw-border-opacity)); } -.col-low-padding { - padding: 0 2px; +.border-neutral-300 { + --tw-border-opacity: 1; + border-color: rgb(212 212 212 / var(--tw-border-opacity)); } -.row-low-padding { - margin: 0 -2px; +.bg-bg { + --tw-bg-opacity: 1; + background-color: rgb(var(--bg) / var(--tw-bg-opacity)); } -.row-mid-padding { - margin: 0 -8px; +.bg-bg-secondary { + --tw-bg-opacity: 1; + background-color: rgb(var(--bg-secondary) / var(--tw-bg-opacity)); } -.row-mid-padding .col-md-6, .row-mid-padding .col-auto { - padding: 0 8px; +.bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254 / var(--tw-bg-opacity)); } -.list-view-table td { - vertical-align: middle; +.bg-button-danger { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-danger) / var(--tw-bg-opacity)); } -.font-weight-boldish { - font-weight: 600; +.bg-button-danger-active { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-danger-active) / var(--tw-bg-opacity)); } -.icon-button { - appearance: none; - -webkit-appearance: none; - -moz-appearance: none; - padding: 0; - border: 0; - background-color: transparent; - font-weight: 700; - line-height: 1; - font-size: 1rem; - text-shadow: 0 1px 0 var(--bg); - opacity: 0.5; +.bg-button-primary { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-primary) / var(--tw-bg-opacity)); } -.icon-button:hover { - opacity: 1; +.bg-button-primary-active { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-primary-active) / var(--tw-bg-opacity)); } -.icon-button span { - vertical-align: top; - line-height: 0.9; +.bg-button-secondary { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-secondary) / var(--tw-bg-opacity)); } -@media (min-width: 768px) { - .col-seventh { - flex: 0 0 calc(100% / 7); - max-width: calc(100% / 7); - } +.bg-button-secondary-active { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-secondary-active) / var(--tw-bg-opacity)); } -.overflow-x{ - overflow-x: auto; - overflow-y: visible; +.bg-button-success { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-success) / var(--tw-bg-opacity)); } -.pagination { - justify-content: center; + +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); } -.cube-id-btn { - border: none; - background-color: var(--bg); - color: var(--text); - margin: -8px; +.bg-gray-500 { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)); } -.cube-id-btn:focus { - outline: 0; + +.bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); } -.small-table td, .small-table th, .small-table li { - padding: .25rem; - font-size: .85rem; +.bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgb(220 252 231 / var(--tw-bg-opacity)); } -.advertisement { - position: relative; - width: 728px; - height: 90px; +.bg-neutral-100 { + --tw-bg-opacity: 1; + background-color: rgb(245 245 245 / var(--tw-bg-opacity)); } -@media (max-width: 991.89px) { - .advertisement { - position: relative; - top: 0; - left: 0; - width: 100%; - min-height: 100px; - } +.bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); } -.sortIcon { - max-height: 1rem; +.bg-teal-100 { + --tw-bg-opacity: 1; + background-color: rgb(204 251 241 / var(--tw-bg-opacity)); } -.legality-badge { - min-width: 6rem; - padding: .6em .4em; - margin-right: .75em; +.bg-transparent { + background-color: transparent; } -.btn:hover { - text-decoration: none !important; -} +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} -.flex-container { - display:flex +.bg-yellow-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 249 195 / var(--tw-bg-opacity)); } -.flex-direction-col { - flex-direction: column +.bg-opacity-75 { + --tw-bg-opacity: 0.75; } -.flex-grow { - flex-grow:1; +.p-0 { + padding: 0px; } -.flex-space-between { - justify-content: space-between; +.p-1 { + padding: 0.25rem; } -.comment-bg-odd { - background-color: var(--bg-dark); - border-top: 1px solid var(--border-light); +.p-2 { + padding: 0.5rem; } -.comment-bg-even { - background-color: var(--bg); - border-top: 1px solid var(--border-light); + +.p-3 { + padding: 0.75rem; } -.header-banner { - flex: 10000 1 auto; - min-width: 0; +.p-4 { + padding: 1rem; } -.banner-image { - height: 40px; margin: 0 0; +.px-0 { + padding-left: 0px; + padding-right: 0px; } -.banner-collapse { - flex: 0 1 auto; +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; } -.footer-ul { - list-style-type: none; +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; } -p.center { - text-align: center; - font-size:12px; +.px-2\.5 { + padding-left: 0.625rem; + padding-right: 0.625rem; } -footer { - background-color: var(--bg-secondary); - bottom: 0; /* stick to bottom */ +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; } -.footer-link { - color: var(--link-secondary); +.px-4 { + padding-left: 1rem; + padding-right: 1rem; } -.footer-link:hover { - color: var(--link-secondary-hover); + +.py-0 { + padding-top: 0px; + padding-bottom: 0px; } -.footer-text { - color: var(--text-secondary); +.py-0\.5 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; } -.footer-header { - color: var(--link-secondary) + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; } -.flex-vertical { - flex-direction: column; +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; } -.flex-row-reverse { - flex-direction: row-reverse; +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; } -.flex-align-stretch { - align-items: stretch +.py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; } -.flex-shrink { - flex-shrink: 1 +.pb-0 { + padding-bottom: 0px; } -div.viewport { - min-height: 100vh; +.pb-1 { + padding-bottom: 0.25rem; } -.bg-dark { - background-color: var(--bg-secondary) !important; +.pb-2 { + padding-bottom: 0.5rem; } -/* these fields are only used on landing, when logged off. no point to overwrite colors */ -.bg-green{ - background-color: #498007; +.pb-3 { + padding-bottom: 0.75rem; } -.bg-light{ - background-color: #F7F7F7; +.pb-4 { + padding-bottom: 1rem; } +.pe-0 { + padding-inline-end: 0px; +} -@media (max-width: 575.98px) { - .landing-half { - min-height: 40vh; - } +.pe-2 { + padding-inline-end: 0.5rem; } -.landing-logo-container { - display: flex; - justify-content: center; - align-items: center; +.pe-3 { + padding-inline-end: 0.75rem; } -.landing-logo { - width: 50%; +.pe-4 { + padding-inline-end: 1rem; } -.landing-btn { - width:50% +.pr-1 { + padding-right: 0.25rem; } -.search-button { - min-width: 10rem; +.ps-0 { + padding-inline-start: 0px; } -@media (max-width: 575.98px) { - .landing-logo { - width: 80%; - } - .landing-btn { - width:100% - } - .search-button { - min-width: 0rem; - } +.ps-2 { + padding-inline-start: 0.5rem; } -.search-bar { - height: calc(1.5em + .75rem + 2px); +.ps-4 { + padding-inline-start: 1rem; } -.article-area { - min-height: 40rem; +.pt-0 { + padding-top: 0px; } -.player-wrapper { - position: relative; - padding-top: 56.25% /* Player ratio: 100 / (1280 / 720) */ +.pt-1 { + padding-top: 0.25rem; } - -.react-player { - position: absolute; - top: 0; - left: 0; + +.pt-2 { + padding-top: 0.5rem; } -audio { - width: 100%; +.pt-3 { + padding-top: 0.75rem; } -.markdown-input { - font-family: "Fira Mono", monospace; - min-height: 20rem !important; +.text-left { + text-align: left; } -.monospaced { - font-family: "Fira Mono", monospace; +.text-center { + text-align: center; } -.card-header { - background-color: var(--header); +.text-start { + text-align: start; } -.dropdown-item.active, .dropdown-item:active { - color: var(--bg); - text-decoration: none; - background-color: var(--success); +.text-end { + text-align: end; } -/*** Styles necessary for Palette Swaps ***/ +.align-middle { + vertical-align: middle; +} -.modal-content, -.dropdown-menu, -.dropdown-menu a, -.dropdown-menu button, -.nav-tabs .nav-item.show .nav-link, -.nav-tabs .nav-link.active, -.bg-white, -.pvtAxisContainer li span.pvtAttr{ - background-color: var(--bg) !important; - color:var(--text); +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; } +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} -.pvtAxisContainer, -.pvtVals{ - background-color: var(--bg) !important; +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} +.text-xs { + font-size: 0.75rem; + line-height: 1rem; } -.form-control, -.form-select, -.form-select:disabled, -.form-control:focus, -.form-select:focus { - background-color: var(--bg); - color:var(--text); - border-color: var(--border); +.font-medium { + font-weight: 500; } -.pvtDropdownCurrent { - background-color: var(--bg) !important; - color:var(--text) !important; - border-color: var(--border) !important; + +.font-normal { + font-weight: 400; } -.input-group-text, .input-group-button { - background-color: var(--bg-dark); - color:var(--text); - border-color: var(--border); +.font-semibold { + font-weight: 600; } -.table { - color:var(--text); - background-color: var(--bg); +.text-blue-800 { + --tw-text-opacity: 1; + color: rgb(30 64 175 / var(--tw-text-opacity)); } -.btn-success { - background-color: var(--button-green); - border-color: var(--button-green); - color: var(--link-secondary); +.text-button-danger { + --tw-text-opacity: 1; + color: rgb(var(--button-danger) / var(--tw-text-opacity)); } -.btn-success:hover { - color: var(--link-secondary); +.text-button-primary { + --tw-text-opacity: 1; + color: rgb(var(--button-primary) / var(--tw-text-opacity)); } -.btn-danger { - background-color: var(--button-red); - border-color: var(--button-red); +.text-button-secondary { + --tw-text-opacity: 1; + color: rgb(var(--button-secondary) / var(--tw-text-opacity)); } -.modal-header, -.modal-footer { - border-color: var(--border-dark); +.text-button-success { + --tw-text-opacity: 1; + color: rgb(var(--button-success) / var(--tw-text-opacity)); } -.nav-tabs, -.border-bottom, -.border-top{ - border-color: var(--border-light) !important; +.text-button-text { + --tw-text-opacity: 1; + color: rgb(var(--button-text) / var(--tw-text-opacity)); } -.navbar-light .navbar-nav .nav-link { - color:var(--secondary-nav-link) +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); } -.navbar-light .navbar-nav .nav-link:hover, -.navbar-light .navbar-nav .nav-link:focus { - color:var(--secondary-nav-link-hover) + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); } -.nav-tabs .nav-item.show .nav-link, -.nav-tabs .nav-link.active, -.nav-tabs .nav-link:hover { - border-color:var(--border-light) var(--border-light) var(--bg) +.text-green-800 { + --tw-text-opacity: 1; + color: rgb(22 101 52 / var(--tw-text-opacity)); } -a { - color: var(--link); +.text-link { + --tw-text-opacity: 1; + color: rgb(var(--link) / var(--tw-text-opacity)); } -a, button.btn-link { - text-decoration: none; +.text-neutral-700 { + --tw-text-opacity: 1; + color: rgb(64 64 64 / var(--tw-text-opacity)); } -.page-item.disabled .page-link { - background-color: var(--bg); +.text-red-800 { + --tw-text-opacity: 1; + color: rgb(153 27 27 / var(--tw-text-opacity)); } -.dropdown-item:focus, -.dropdown-item:hover { - background-color: var(--bg); +.text-teal-800 { + --tw-text-opacity: 1; + color: rgb(17 94 89 / var(--tw-text-opacity)); } -.dropdown-item:focus, -.dropdown-item:hover{ - color:var(--dropdown-hover); +.text-text { + --tw-text-opacity: 1; + color: rgb(var(--text) / var(--tw-text-opacity)); } -.border-start{ - border-left: 1px solid var(--border-light) !important; +.text-text-secondary { + --tw-text-opacity: 1; + color: rgb(var(--text-secondary) / var(--tw-text-opacity)); } -th[scope=row] { - position: -webkit-sticky; - position: sticky; - left: 0; - z-index: 1; - background: linear-gradient(90deg, var(--border) 0%, var(--border) calc(0% + .05em), transparent calc(0% + .05em), transparent calc(100% - .05em), var(--border) calc(100% - .05em), var(--border) 100%); - background-color: var(--bg); - border-left: 0px; - border-right: 0px; +.text-text-secondary-active { + --tw-text-opacity: 1; + color: rgb(var(--text-secondary-active) / var(--tw-text-opacity)); } -thead th[scope=col] { - position: -webkit-sticky; - position: sticky; - top: 0px; - z-index: 2; - background: linear-gradient(0deg, var(--border) 0%, var(--border) calc(0% + .05em), transparent calc(0% + .05em), transparent calc(100% - .05em), var(--border) calc(100% - .05em), var(--border) 100%); - background-color: var(--bg); + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); } -thead th.corner[scope=col] { - left: 0; - z-index: 3; - background: linear-gradient(90deg, var(--border) 0%, var(--border) calc(0% + .05em), transparent calc(0% + .05em), transparent calc(100% - .05em), var(--border) calc(100% - .05em), var(--border) 100%); - background-color: var(--bg); - border-left: 0px; - border-right: 0px; + +.text-yellow-800 { + --tw-text-opacity: 1; + color: rgb(133 77 14 / var(--tw-text-opacity)); } -.table-responsive { - overflow-y: auto; - overflow-x: auto; - width: 100%; - display: block; - max-height: calc(100vh - 4em); + +.placeholder-text-secondary::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(var(--text-secondary) / var(--tw-placeholder-opacity)); } -.table thead th, -.table-bordered td, -.table-bordered th { - border-color: var(--border); +.opacity-0 { + opacity: 0; } -.blog-post-border { - border-right: 1px solid var(--border-light); + +.opacity-100 { + opacity: 1; } -.form-control:disabled, .form-control[readOnly] { - background-color: var(--read-only-disabled); +.opacity-50 { + opacity: 0.5; } -.pvtUi, -.pvtTable { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important +.shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.cubes-card-header { - display: flex; - justify-content: space-between; - align-items: baseline; +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.pack-outline { - background-color: var(--bg-dark); +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.bg-advert { - background-color: var(--bg-advert); +.shadow-xl { + --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.main-content { - position:relative; + +.\!outline { + outline-style: solid !important; } -.max-width-1600 { - max-width: 1600px; + +.outline { + outline-style: solid; } -.ad-right { - text-align: center; - float: right; - width: 160px; - position:absolute; - right:-170px; - top:10px; - height: 100% +.blur { + --tw-blur: blur(8px); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } - -.ad-left { - text-align: center; - float: right; - width: 160px; - position:absolute; - left:-170px; - top:10px; - height: 100% + +.invert { + --tw-invert: invert(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } - -.advertisement-div { - height: 50% + +.\!filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) !important; } -.qr-code-area { - background-color: #FFFFFF !important +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } -div.advertisement-div > div:not(.report-link) { - height: 100% !important +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-opacity { + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.duration-200 { + transition-duration: 200ms; +} + +.duration-300 { + transition-duration: 300ms; +} + +.ease-in { + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); +} + +.ease-in-out { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +.ease-out { + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} + +.dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; +} + +@media (min-width: 640px) { + .sm\:container { + width: 100%; + } + + @media (min-width: 640px) { + .sm\:container { + max-width: 640px; + } + } + + @media (min-width: 768px) { + .sm\:container { + max-width: 768px; + } + } + + @media (min-width: 1024px) { + .sm\:container { + max-width: 1024px; + } } -.modal-dialog { - padding-bottom: 60px; + @media (min-width: 1280px) { + .sm\:container { + max-width: 1280px; + } + } + + @media (min-width: 1536px) { + .sm\:container { + max-width: 1536px; + } + } } -/* Shims for .badge-* until we replace changelogs with a React component */ -.badge.badge-success { - background-color: var(--success); +@media (min-width: 768px) { + .md\:container { + width: 100%; + } + + @media (min-width: 640px) { + .md\:container { + max-width: 640px; + } + } + + @media (min-width: 768px) { + .md\:container { + max-width: 768px; + } + } + + @media (min-width: 1024px) { + .md\:container { + max-width: 1024px; + } + } + + @media (min-width: 1280px) { + .md\:container { + max-width: 1280px; + } + } + + @media (min-width: 1536px) { + .md\:container { + max-width: 1536px; + } + } } -.badge.badge-danger { - background-color: var(--danger); + +@media (min-width: 1024px) { + .lg\:container { + width: 100%; + } + + @media (min-width: 640px) { + .lg\:container { + max-width: 640px; + } + } + + @media (min-width: 768px) { + .lg\:container { + max-width: 768px; + } + } + + @media (min-width: 1024px) { + .lg\:container { + max-width: 1024px; + } + } + + @media (min-width: 1280px) { + .lg\:container { + max-width: 1280px; + } + } + + @media (min-width: 1536px) { + .lg\:container { + max-width: 1536px; + } + } } -.badge.badge-primary { - background-color: #007bff; /* bootstrap primary color */ + +@media (min-width: 1280px) { + .xl\:container { + width: 100%; + } + + @media (min-width: 640px) { + .xl\:container { + max-width: 640px; + } + } + + @media (min-width: 768px) { + .xl\:container { + max-width: 768px; + } + } + + @media (min-width: 1024px) { + .xl\:container { + max-width: 1024px; + } + } + + @media (min-width: 1280px) { + .xl\:container { + max-width: 1280px; + } + } + + @media (min-width: 1536px) { + .xl\:container { + max-width: 1536px; + } + } } -#notice-alert .markdown { - padding: 0; - margin: 0; +@media (min-width: 1536px) { + .\32xl\:container { + width: 100%; + } + + @media (min-width: 640px) { + .\32xl\:container { + max-width: 640px; + } + } + + @media (min-width: 768px) { + .\32xl\:container { + max-width: 768px; + } + } + + @media (min-width: 1024px) { + .\32xl\:container { + max-width: 1024px; + } + } + + @media (min-width: 1280px) { + .\32xl\:container { + max-width: 1280px; + } + } + + @media (min-width: 1536px) { + .\32xl\:container { + max-width: 1536px; + } + } +} + +.hover\:cursor-pointer:hover { + cursor: pointer; +} + +.hover\:border-button-success-active:hover { + --tw-border-opacity: 1; + border-color: rgb(var(--button-success-active) / var(--tw-border-opacity)); +} + +.hover\:bg-bg-active:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--bg-active) / var(--tw-bg-opacity)); +} + +.hover\:bg-button-danger:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-danger) / var(--tw-bg-opacity)); +} + +.hover\:bg-button-danger-active:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-danger-active) / var(--tw-bg-opacity)); +} + +.hover\:bg-button-primary:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-primary) / var(--tw-bg-opacity)); +} + +.hover\:bg-button-primary-active:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-primary-active) / var(--tw-bg-opacity)); } -#notice-alert .markdown p:last-child { - padding-bottom: 0; - margin-bottom: 0; +.hover\:bg-button-secondary:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-secondary) / var(--tw-bg-opacity)); } -.rightArrowBox:after { - content: ' '; - width:0px; - height:0px; - border-left: 1.6rem solid var(--border-light); - border-top: 1.4rem solid transparent; - border-bottom: 1.4rem solid transparent; - border-right: 1.6rem solid transparent; - position:absolute; - left: 100%; +.hover\:bg-button-secondary-active:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-secondary-active) / var(--tw-bg-opacity)); } -.leftArrowBox:after { - content: ' '; - width:0px; - height:0px; - border-left: 1.6rem solid transparent; - border-top: 1.4rem solid transparent; - border-bottom: 1.4rem solid transparent; - border-right: 1.6rem solid var(--border-light); - position:absolute; - right: 100%; +.hover\:bg-button-success:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-success) / var(--tw-bg-opacity)); } -.noBreak { - white-space: nowrap; +.hover\:bg-button-success-active:hover { + --tw-bg-opacity: 1; + background-color: rgb(var(--button-success-active) / var(--tw-bg-opacity)); } -.boardTitle { - text-transform: capitalize; +.hover\:text-button-text:hover { + --tw-text-opacity: 1; + color: rgb(var(--button-text) / var(--tw-text-opacity)); +} + +.hover\:text-link-active:hover { + --tw-text-opacity: 1; + color: rgb(var(--link-active) / var(--tw-text-opacity)); +} + +.hover\:text-text-secondary:hover { + --tw-text-opacity: 1; + color: rgb(var(--text-secondary) / var(--tw-text-opacity)); +} + +.hover\:text-text-secondary-active:hover { + --tw-text-opacity: 1; + color: rgb(var(--text-secondary-active) / var(--tw-text-opacity)); +} + +.focus\:border-button-text:focus { + --tw-border-opacity: 1; + border-color: rgb(var(--button-text) / var(--tw-border-opacity)); +} + +.focus\:border-focus-ring:focus { + --tw-border-opacity: 1; + border-color: rgb(var(--focus-ring) / var(--tw-border-opacity)); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-focus-ring:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(var(--focus-ring) / var(--tw-ring-opacity)); +} + +.focus\:ring-opacity-50:focus { + --tw-ring-opacity: 0.5; +} + +@media (min-width: 640px) { + .sm\:mx-auto { + margin-left: auto; + margin-right: auto; + } + + .sm\:block { + display: block; + } + + .sm\:hidden { + display: none; + } + + .sm\:w-1\/2 { + width: 50%; + } + + .sm\:translate-y-0 { + --tw-translate-y: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + + .sm\:scale-100 { + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + + .sm\:scale-95 { + --tw-scale-x: .95; + --tw-scale-y: .95; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + + .sm\:text-sm { + font-size: 0.875rem; + line-height: 1.25rem; + } +} + +@media (min-width: 768px) { + .md\:mx-auto { + margin-left: auto; + margin-right: auto; + } + + .md\:block { + display: block; + } + + .md\:hidden { + display: none; + } + + .md\:w-1\/4 { + width: 25%; + } +} + +@media (min-width: 1024px) { + .lg\:mx-auto { + margin-left: auto; + margin-right: auto; + } + + .lg\:block { + display: block; + } + + .lg\:hidden { + display: none; + } +} + +@media (min-width: 1280px) { + .xl\:mx-auto { + margin-left: auto; + margin-right: auto; + } + + .xl\:block { + display: block; + } + + .xl\:hidden { + display: none; + } +} + +@media (min-width: 1536px) { + .\32xl\:mx-auto { + margin-left: auto; + margin-right: auto; + } + + .\32xl\:block { + display: block; + } + + .\32xl\:hidden { + display: none; + } } diff --git a/routes/users_routes.js b/routes/users_routes.js index 00e9095c4..b00413964 100644 --- a/routes/users_routes.js +++ b/routes/users_routes.js @@ -71,14 +71,21 @@ router.get('/notification/:id', ensureAuth, async (req, res) => { router.post('/clearnotifications', ensureAuth, async (req, res) => { try { - const notifications = await Notification.getByToAndStatus(`${req.user.id}`, Notification.STATUS.UNREAD); + let items, lastKey; - await Notification.batchPut( - notifications.items.map((notification) => ({ - ...notification, - status: Notification.STATUS.READ, - })), - ); + do { + const result = await Notification.getByToAndStatus(`${req.user.id}`, Notification.STATUS.UNREAD, lastKey); + + items = result.items; + lastKey = result.lastKey; + + await Notification.batchPut( + items.map((notification) => ({ + ...notification, + status: Notification.STATUS.READ, + })), + ); + } while (lastKey); return res.status(200).send({ success: 'true', diff --git a/serverjs/render.js b/serverjs/render.js index f2a3f3d51..38c63d8cb 100644 --- a/serverjs/render.js +++ b/serverjs/render.js @@ -49,17 +49,16 @@ const render = (req, res, page, reactProps = {}, options = {}) => { } try { - const theme = (req && req.user && req.user.theme) || 'default'; + const theme =(req && req.user && req.user.theme) || 'default'; res.render('main', { reactHTML: null, // TODO renable ReactDOMServer.renderToString(React.createElement(page, reactProps)), reactProps: serialize(reactProps), page, metadata: options.metadata, title: options.title ? `${options.title} - Cube Cobra` : 'Cube Cobra', - colors: `/css/${theme}.css`, - bootstrap: `/css/bootstrap/bs-${theme}.css`, patron: req.user && (req.user.roles || []).includes('Patron'), notice: process.env.NOTICE, + theme }); } catch { res.status(500).send('Error rendering page'); diff --git a/src/components/BlogContextMenu.tsx b/src/components/BlogContextMenu.tsx index ad1806a37..ec0d67e23 100644 --- a/src/components/BlogContextMenu.tsx +++ b/src/components/BlogContextMenu.tsx @@ -29,7 +29,7 @@ const BlogContextMenu: React.FC = ({ post, value, onEdit } return ( <> - + {value} diff --git a/src/components/CardSearchBar.tsx b/src/components/CardSearchBar.tsx index cb1601bc7..8671e6c4f 100644 --- a/src/components/CardSearchBar.tsx +++ b/src/components/CardSearchBar.tsx @@ -1,17 +1,17 @@ import React from 'react'; -import { Button, Input, InputGroup } from 'reactstrap'; +import Flexbox from './base/Flexbox'; +import Input from './base/Input'; +import Button from './base/Button'; const CardSearchBar: React.FC = () => { return (
-
- - - - -
+ + + +
); }; diff --git a/src/components/Comment.tsx b/src/components/Comment.tsx index a41b64b72..b4ad67eea 100644 --- a/src/components/Comment.tsx +++ b/src/components/Comment.tsx @@ -5,7 +5,7 @@ import TimeAgo from 'react-timeago'; import CommentContextMenu from 'components/CommentContextMenu'; import CommentEntry from 'components/CommentEntry'; -import LinkButton from 'components/LinkButton'; +import LinkButton from 'components/base/LinkButton'; import Markdown from 'components/Markdown'; import ReportCommentModal from 'components/ReportCommentModal'; import ShareCommentModal from 'components/ShareCommentModal'; diff --git a/src/components/CommentContextMenu.tsx b/src/components/CommentContextMenu.tsx index aad4ffea3..d6d32cbe5 100644 --- a/src/components/CommentContextMenu.tsx +++ b/src/components/CommentContextMenu.tsx @@ -14,7 +14,7 @@ const CommentContextMenu: React.FC = ({ edit, remove, c return ( - + {children} diff --git a/src/components/CommentEntry.tsx b/src/components/CommentEntry.tsx index 73ab11264..6bbf4001a 100644 --- a/src/components/CommentEntry.tsx +++ b/src/components/CommentEntry.tsx @@ -1,7 +1,7 @@ import React, { Dispatch, SetStateAction, useState } from 'react'; import { Collapse } from 'reactstrap'; -import LinkButton from 'components/LinkButton'; +import LinkButton from 'components/base/LinkButton'; export interface CommentEntryProps { submit: (text: string) => void; diff --git a/src/components/CommentsSection.tsx b/src/components/CommentsSection.tsx index 840811fab..470a32a52 100644 --- a/src/components/CommentsSection.tsx +++ b/src/components/CommentsSection.tsx @@ -2,7 +2,7 @@ import React, { useContext } from 'react'; import { Button, Collapse, Spinner } from 'reactstrap'; import CommentEntry from 'components/CommentEntry'; -import LinkButton from 'components/LinkButton'; +import LinkButton from 'components/base/LinkButton'; import CommentList from 'components/PagedCommentList'; import UserContext from 'contexts/UserContext'; import useComments from 'hooks/UseComments'; diff --git a/src/components/LinkButton.js b/src/components/LinkButton.js deleted file mode 100644 index cc1a350ce..000000000 --- a/src/components/LinkButton.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - -import PropTypes from 'prop-types'; - -const LinkButton = ({ children, onClick, ...props }) => { - return ( - { - event.preventDefault(); - onClick(); - }} - {...props} - > - {children} - - ); -}; -LinkButton.propTypes = { - onClick: PropTypes.func.isRequired, - children: PropTypes.node.isRequired, -}; - -export default LinkButton; diff --git a/src/components/LoginModal.js b/src/components/LoginModal.js deleted file mode 100644 index a27eb8791..000000000 --- a/src/components/LoginModal.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { Button, Col, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader, Row } from 'reactstrap'; - -import PropTypes from 'prop-types'; - -import CSRFForm from 'components/CSRFForm'; - -const LoginModal = ({ isOpen, toggle, loginCallback }) => ( - - Login - - - - - - - - - - - - - - - - - - - - - - - - - Forgot password? - - - - - - - - - - -); - -LoginModal.propTypes = { - isOpen: PropTypes.bool.isRequired, - toggle: PropTypes.func.isRequired, - loginCallback: PropTypes.string.isRequired, -}; - -export default LoginModal; diff --git a/src/components/LoginModal.tsx b/src/components/LoginModal.tsx new file mode 100644 index 000000000..3a744e77a --- /dev/null +++ b/src/components/LoginModal.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import CSRFForm from 'components/CSRFForm'; +import Input from 'components/base/Input'; +import Button from 'components/base/Button'; + +import { Modal, ModalHeader, ModalBody, ModalFooter } from 'components/base/Modal'; +import Flexbox from './base/Flexbox'; + +interface LoginModalProps { + isOpen: boolean; + setOpen: (open: boolean) => void; + loginCallback: string; +} + +const LoginModal: React.FC = ({ isOpen, setOpen, loginCallback }) => ( + + Login + + + + + + + + + + + + + +); + +export default LoginModal; diff --git a/src/components/NotificationsNav.js b/src/components/NotificationsNav.js deleted file mode 100644 index a0a76d710..000000000 --- a/src/components/NotificationsNav.js +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useContext, useState } from 'react'; -import { Badge, CardFooter, CardHeader, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap'; - -import { BellFillIcon } from '@primer/octicons-react'; - -import LinkButton from 'components/LinkButton'; -import UserContext from 'contexts/UserContext'; -import { csrfFetch } from 'utils/CSRF'; - -const NotificationsNav = () => { - const { notifications } = useContext(UserContext); - - const [items, setItems] = useState(notifications); - - const clear = async () => { - await csrfFetch('/user/clearnotifications', { - method: 'POST', - }); - setItems([]); - }; - - return ( - - - {items.length > 0 && {items.length > 30 ? '30+' : items.length}} - - - - - - -
- Notifications - {items.length > 0 && ( - - Clear All - - )} -
-
-
- {items.length > 0 ? ( - items.map((notification) => ( - - )) - ) : ( -
- You don't have any notifications to show. -
- )} -
- -
- - View Older Notifications - -
-
-
-
- ); -}; - -export default NotificationsNav; diff --git a/src/components/RenderToRoot.tsx b/src/components/RenderToRoot.tsx index cca433409..11bead894 100644 --- a/src/components/RenderToRoot.tsx +++ b/src/components/RenderToRoot.tsx @@ -18,6 +18,7 @@ export interface UniversalReactProps { nitroPayEnabled: boolean; domain: DomainContextValue; user: UserContextValue; + theme: string; } // Returns its input to enable our usual pattern of export default RenderToRoot(XPage). diff --git a/src/components/SampleHandModal.js b/src/components/SampleHandModal.js index 0f2a64fcc..c0eb3d387 100644 --- a/src/components/SampleHandModal.js +++ b/src/components/SampleHandModal.js @@ -76,7 +76,7 @@ class SampleHandModal extends Component { return ( <> - + Sample Hand diff --git a/src/components/WithModal.tsx b/src/components/WithModal.tsx index 149f2cd6e..53ec318cd 100644 --- a/src/components/WithModal.tsx +++ b/src/components/WithModal.tsx @@ -44,7 +44,7 @@ const withModal = (Tag: T, ModalTag: ComponentType) > {children} - + ); }; diff --git a/src/components/base/Badge.tsx b/src/components/base/Badge.tsx new file mode 100644 index 000000000..4411bfcbb --- /dev/null +++ b/src/components/base/Badge.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import classNames from 'classnames'; + +interface BadgeProps { + color?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark'; + pill?: boolean; + className?: string; + children: React.ReactNode; +} + +const Badge: React.FC = ({ color = 'primary', pill = false, className, children }) => { + return ( + + {children} + + ); +}; + +export default Badge; diff --git a/src/components/base/Button.tsx b/src/components/base/Button.tsx new file mode 100644 index 000000000..88c30db89 --- /dev/null +++ b/src/components/base/Button.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import classNames from 'classnames'; + +interface ButtonProps { + children: React.ReactNode; + color?: 'success' | 'danger' | 'primary' | 'secondary'; + outline?: boolean; + disabled?: boolean; + onClick?: () => void; + type?: 'button' | 'submit' | 'reset'; + block?: boolean; +} + +const Button: React.FC = ({ + children, + color = 'primary', + outline = false, + disabled = false, + onClick, + type, + block, +}) => { + const classes = classNames( + 'px-2 py-1 rounded focus:outline-none font-semibold transition-colors duration-300 ease-in-out border focus:border-button-text', + { + 'text-button-text': ['success', 'danger', 'primary', 'secondary'].includes(color) && !outline, + 'text-button-text-secondary': !['success', 'danger', 'primary', 'secondary'].includes(color) && !outline, + 'text-button-success bg-transparent border border-button-success hover:bg-button-success hover:text-button-text': + outline && color == 'success', + 'bg-button-success border-button-success hover:bg-button-success-active hover:border-button-success-active': + !outline && color == 'success', + 'text-button-danger bg-transparent border border-button-danger hover:bg-button-danger hover:text-button-text': + outline && color == 'danger', + 'bg-button-danger border-button-danger hover:bg-button-danger-active': !outline && color == 'danger', + 'text-button-primary bg-transparent border border-button-primary hover:bg-button-primary hover:text-button-text': + outline && color == 'primary', + 'bg-button-primary border-button-primary hover:bg-button-primary-active': !outline && color == 'primary', + 'text-button-secondary bg-transparent border border-button-secondary hover:bg-button-secondary hover:text-button-text': + outline && color == 'secondary', + 'bg-button-secondary border-button-secondary hover:bg-button-secondary-active': !outline && color == 'secondary', + 'opacity-50 cursor-not-allowed': disabled, + 'focus:ring-2 focus:ring-focus-ring focus:ring-opacity-50 focus:border-focus-ring': true, + 'w-full': block, + }, + ); + + return ( + + ); +}; + +export default Button; diff --git a/src/components/base/Card.tsx b/src/components/base/Card.tsx new file mode 100644 index 000000000..3848f83e4 --- /dev/null +++ b/src/components/base/Card.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import classNames from 'classnames'; + +interface CardProps { + className?: string; + children: React.ReactNode; +} + +interface CardHeaderProps { + className?: string; + children: React.ReactNode; +} + +interface CardFooterProps { + className?: string; + children: React.ReactNode; +} + +const Card: React.FC = ({ className, children }) => { + return
{children}
; +}; + +const CardHeader: React.FC = ({ className, children }) => { + return
{children}
; +}; + +const CardFooter: React.FC = ({ className, children }) => { + return
{children}
; +}; + +export { Card, CardHeader, CardFooter }; diff --git a/src/components/base/Collapse.tsx b/src/components/base/Collapse.tsx new file mode 100644 index 000000000..dc4f10a8d --- /dev/null +++ b/src/components/base/Collapse.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +interface CollapseProps { + isOpen: boolean; + children: React.ReactNode; +} + +const Collapse: React.FC = ({ isOpen, children }) => { + return ( +
+ {children} +
+ ); +}; + +export default Collapse; diff --git a/src/components/base/Container.tsx b/src/components/base/Container.tsx new file mode 100644 index 000000000..4d4dee211 --- /dev/null +++ b/src/components/base/Container.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import classNames from 'classnames'; + +interface ContainerProps { + children: React.ReactNode; + className?: string; + sm?: boolean; + md?: boolean; + lg?: boolean; + xl?: boolean; + xxl?: boolean; +} + +const Container: React.FC = ({ children, className = '', sm, md, lg, xl, xxl }) => { + const classes = classNames( + 'w-full', + { + 'sm:container sm:mx-auto': sm, + 'md:container md:mx-auto': md, + 'lg:container lg:mx-auto': lg, + 'xl:container xl:mx-auto': xl, + '2xl:container 2xl:mx-auto': xxl, + }, + className, + ); + + return
{children}
; +}; + +export default Container; diff --git a/src/components/base/Flexbox.tsx b/src/components/base/Flexbox.tsx new file mode 100644 index 000000000..d6022a40d --- /dev/null +++ b/src/components/base/Flexbox.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import classNames from 'classnames'; + +interface FlexboxProps { + direction?: 'row' | 'row-reverse' | 'col' | 'col-reverse'; + wrap?: 'wrap' | 'wrap-reverse' | 'nowrap'; + justify?: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'; + alignItems?: 'start' | 'end' | 'center' | 'baseline' | 'stretch'; + alignContent?: 'start' | 'end' | 'center' | 'between' | 'around' | 'stretch'; + gap?: + | '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + | '10' + | '11' + | '12' + | '14' + | '16' + | '20' + | '24' + | '28' + | '32' + | '36' + | '40' + | '44' + | '48' + | '52' + | '56' + | '60' + | '64' + | '72' + | '80' + | '96'; + children: React.ReactNode; + className?: string; +} + +const Flexbox: React.FC = ({ + direction = 'row', + wrap = 'nowrap', + justify = 'start', + alignItems = 'stretch', + alignContent = 'stretch', + gap = '0', + children, + className = '', +}) => { + const classes = classNames( + 'flex', + `flex-${direction}`, + `flex-${wrap}`, + `justify-${justify}`, + `items-${alignItems}`, + `content-${alignContent}`, + `gap-${gap}`, + className, + ); + + return
{children}
; +}; + +export default Flexbox; diff --git a/src/components/base/Input.tsx b/src/components/base/Input.tsx new file mode 100644 index 000000000..462ba7bf8 --- /dev/null +++ b/src/components/base/Input.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import classNames from 'classnames'; +import Flexbox from './Flexbox'; + +interface InputProps extends React.InputHTMLAttributes { + className?: string; + label?: string; + link?: { + href: string; + text: string; + }; +} + +const Input: React.FC = ({ className, label, link, ...props }) => { + return ( +
+ + {label && ( + + )} + {link && ( + + {link.text} + + )} + + +
+ ); +}; + +export default Input; diff --git a/src/components/base/LinkButton.tsx b/src/components/base/LinkButton.tsx new file mode 100644 index 000000000..9ce598239 --- /dev/null +++ b/src/components/base/LinkButton.tsx @@ -0,0 +1,26 @@ +import React, { MouseEventHandler } from 'react'; +import classNames from 'classnames'; + +interface LinkButtonProps extends React.AnchorHTMLAttributes { + onClick: MouseEventHandler; + children: React.ReactNode; +} + +const LinkButton: React.FC = ({ children, onClick }) => { + return ( + { + event.preventDefault(); + onClick(event); + }} + className={classNames( + 'select-none cursor-pointer font-normal transition-colors duration-200 ease-in-out text-text hover:text-text-secondary', + )} + > + {children} + + ); +}; + +export default LinkButton; diff --git a/src/components/base/LinkDiv.tsx b/src/components/base/LinkDiv.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/base/Modal.tsx b/src/components/base/Modal.tsx new file mode 100644 index 000000000..2db6a03c3 --- /dev/null +++ b/src/components/base/Modal.tsx @@ -0,0 +1,109 @@ +import React, { ReactNode } from 'react'; +import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react'; +import classNames from 'classnames'; + +import { XIcon } from '@primer/octicons-react'; + +interface ModalProps { + children: ReactNode; + isOpen: boolean; + setOpen: (open: boolean) => void; + sm?: boolean; + md?: boolean; + lg?: boolean; + xl?: boolean; +} + +export const Modal: React.FC = ({ children, isOpen, setOpen, sm, md, lg, xl }) => { + return ( + + setOpen(false)}> + +
+ +
+
+
+ +
+ + {children} + +
+
+
+
+
+
+
+ ); +}; + +interface ModalHeaderProps { + children: ReactNode; + className?: string; + setOpen: (open: boolean) => void; +} + +export const ModalHeader: React.FC = ({ children, className, setOpen }) => { + return ( +
+ {children} + +
+ ); +}; + +interface ModalFooterProps { + children: ReactNode; + className?: string; +} + +export const ModalFooter: React.FC = ({ children, className }) => { + return
{children}
; +}; + +interface ModalBodyProps { + children: React.ReactNode; + className?: string; + fixed?: boolean; +} + +export const ModalBody: React.FC = ({ children, className = '', fixed = false }) => { + return ( +
+ {children} +
+ ); +}; diff --git a/src/components/base/NavButton.tsx b/src/components/base/NavButton.tsx new file mode 100644 index 000000000..94a8cf4c8 --- /dev/null +++ b/src/components/base/NavButton.tsx @@ -0,0 +1,25 @@ +import classNames from 'classnames'; +import React from 'react'; + +interface NavButtonProps { + children: React.ReactNode; + className?: string; + root?: boolean; + onClick?: () => void; +} + +const NavButton: React.FC = ({ children, onClick, root = false }) => { + return ( + + {children} + + ); +}; + +export default NavButton; diff --git a/src/components/base/NavLink.tsx b/src/components/base/NavLink.tsx new file mode 100644 index 000000000..120cfbdbe --- /dev/null +++ b/src/components/base/NavLink.tsx @@ -0,0 +1,25 @@ +import classNames from 'classnames'; +import React from 'react'; + +interface NavLinkProps { + href: string; + children: React.ReactNode; + className?: string; + root?: boolean; +} + +const NavLink: React.FC = ({ href, children, root = false }) => { + return ( + + {children} + + ); +}; + +export default NavLink; diff --git a/src/components/base/NavMenu.tsx b/src/components/base/NavMenu.tsx new file mode 100644 index 000000000..a08e4346d --- /dev/null +++ b/src/components/base/NavMenu.tsx @@ -0,0 +1,84 @@ +import React, { useState, useRef, useEffect } from 'react'; +import ResponsiveDiv from './ResponsiveDiv'; +import Flexbox from './Flexbox'; +import classNames from 'classnames'; +// import Collapse from './Collapse'; + +interface NavMenuProps { + label: React.ReactNode; + children: React.ReactNode; + wide?: boolean; +} + +const NavMenu: React.FC = ({ label, children, wide }) => { + const [isOpen, setIsOpen] = useState(false); + const menuRef = useRef(null); + + const handleClickOutside = (event: MouseEvent) => { + if (menuRef.current && !menuRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( +
+ {/* Mobile */} + + setIsOpen(!isOpen)} + > + {label} + + {isOpen && ( +
+ + {children} + +
+ )} +
+ + {/* Desktop */} + + setIsOpen(!isOpen)} + > + {label} + +
+ {children} +
+
+
+ ); +}; + +export default NavMenu; diff --git a/src/components/base/ResponsiveDiv.tsx b/src/components/base/ResponsiveDiv.tsx new file mode 100644 index 000000000..b8d75327f --- /dev/null +++ b/src/components/base/ResponsiveDiv.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import classNames from 'classnames'; + +interface ResponsiveDivProps { + children: React.ReactNode; + baseVisible?: boolean; // Determines if the base class is `block` or `hidden` + sm?: boolean; + md?: boolean; + lg?: boolean; + xl?: boolean; + xxl?: boolean; + className?: string; +} + +const ResponsiveDiv: React.FC = ({ + children, + baseVisible = false, + sm, + md, + lg, + xl, + xxl, + className = '', +}) => { + const classes = classNames( + baseVisible ? 'block' : 'hidden', + { + 'sm:block': sm && !baseVisible, + 'sm:hidden': sm && baseVisible, + 'md:block': md && !baseVisible, + 'md:hidden': md && baseVisible, + 'lg:block': lg && !baseVisible, + 'lg:hidden': lg && baseVisible, + 'xl:block': xl && !baseVisible, + 'xl:hidden': xl && baseVisible, + '2xl:block': xxl && !baseVisible, + '2xl:hidden': xxl && baseVisible, + }, + className, + ); + + return
{children}
; +}; + +export default ResponsiveDiv; diff --git a/src/components/nav/Navbar.tsx b/src/components/nav/Navbar.tsx new file mode 100644 index 000000000..e087cb924 --- /dev/null +++ b/src/components/nav/Navbar.tsx @@ -0,0 +1,164 @@ +import React, { useContext } from 'react'; + +import { ChevronUpIcon, ThreeBarsIcon } from '@primer/octicons-react'; + +import { CardFooter } from 'components/base/Card'; +import NavButton from 'components/base/NavButton'; +import CardSearchBar from 'components/CardSearchBar'; +import LoginModal from 'components/LoginModal'; +import UserContext from 'contexts/UserContext'; +import Button from '../base/Button'; +import Collapse from '../base/Collapse'; +import Container from '../base/Container'; +import Flexbox from '../base/Flexbox'; +import NavLink from '../base/NavLink'; +import NavMenu from '../base/NavMenu'; +import ResponsiveDiv from '../base/ResponsiveDiv'; +import NotificationsNav from './NotificationsNav'; + +import withModal from 'components/WithModal'; + +const LoginButton = withModal(NavButton, LoginModal); + +const navItems = [ + { + title: 'Content', + items: [ + { label: 'Browse', href: '/content/browse' }, + { label: 'Articles', href: '/content/articles' }, + { label: 'Podcasts', href: '/content/podcasts' }, + { label: 'Videos', href: '/content/videos' }, + ], + }, + { + title: 'Cube', + items: [ + { label: 'Explore cubes', href: '/cube/explore' }, + { label: 'Search cubes', href: '/cube/search' }, + ], + }, + { + title: 'Cards', + items: [ + { label: 'Top Cards', href: '/tool/topcards' }, + { label: 'Search Cards', href: '/tool/searchcards' }, + { label: 'Packages', href: '/packages/approved' }, + { label: 'Filter Syntax', href: '/filters' }, + ], + }, + { + title: 'About', + items: [ + { label: 'Dev Blog', href: '/dev/blog' }, + { label: 'Contact', href: '/contact' }, + { + label: 'Merchandise', + href: 'https://www.inkedgaming.com/collections/artists-gwen-dekker?rfsn=4250904.d3f372&utm_source=refersion&utm_medium=affiliate&utm_campaign=4250904.d3f372', + }, + { label: 'Donate', href: '/about/donate' }, + { label: 'Github', href: 'https://github.com/dekkerglen/CubeCobra' }, + ], + }, +]; + +type NavbarProps = { + expanded: boolean; + toggle: () => void; + loginCallback?: string; +}; + +const Navbar: React.FC = ({ toggle, expanded }) => { + const user = useContext(UserContext); + + const navs = ( + <> + {navItems.map((item) => ( + + + {item.items.map((subItem) => ( + {subItem.label} + ))} + + + ))} + {user ? ( + <> + + + + {(user.cubes || []).map((item) => ( + + {item.name} + + ))} + + + Create A New Cube + + + + + Your Profile + {user.roles && user.roles.includes('Admin') && Admin Page} + {user.roles && user.roles.includes('ContentCreator') && ( + Content Creator Dashboard + )} + Create A New Cube + Social + Account Information + Logout + + + + ) : ( + <> + + Register + + Login + + )} + + ); + + return ( + //
+
+ + + + + Cube Cobra: a site for Magic: the Gathering Cubing + + + + + + + + + + {navs} + + + + + + + {navs} + + + + + +
+ ); +}; + +export default Navbar; diff --git a/src/components/Notification.js b/src/components/nav/Notification.js similarity index 100% rename from src/components/Notification.js rename to src/components/nav/Notification.js diff --git a/src/components/nav/NotificationsNav.tsx b/src/components/nav/NotificationsNav.tsx new file mode 100644 index 000000000..2e9aadd82 --- /dev/null +++ b/src/components/nav/NotificationsNav.tsx @@ -0,0 +1,70 @@ +import React, { useContext, useState } from 'react'; +import { BellFillIcon } from '@primer/octicons-react'; +import LinkButton from 'components/base/LinkButton'; +import UserContext from 'contexts/UserContext'; +import { csrfFetch } from 'utils/CSRF'; +import { Notification } from 'datatypes/Notification'; +import Badge from 'components/base/Badge'; +import { CardHeader, CardFooter } from 'components/base/Card'; +import NavMenu from 'components/base/NavMenu'; +import Flexbox from 'components/base/Flexbox'; +import { NavLink } from 'reactstrap'; + +const NotificationsNav: React.FC = () => { + const user = useContext(UserContext); + + if (!user) { + return null; + } + const [items, setItems] = useState(user.notifications || []); + + const clear = async () => { + await csrfFetch('/user/clearnotifications', { + method: 'POST', + }); + setItems([]); + }; + + const label = ( + <> + {items.length > 0 && {items.length > 30 ? '30+' : items.length}} + + + ); + + return ( + + + + + Notifications + + Clear All + + + + + {items.length > 0 ? ( + items.map((notification) => ( + + {notification.body} + + )) + ) : ( +
+ You don't have any notifications to show. +
+ )} +
+ + View Older Notifications + +
+
+ ); +}; + +export default NotificationsNav; diff --git a/src/contexts/ThemeContext.ts b/src/contexts/ThemeContext.ts deleted file mode 100644 index d0cf00918..000000000 --- a/src/contexts/ThemeContext.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createContext } from 'react'; - -const ThemeContext = createContext('default'); - -export default ThemeContext; diff --git a/src/css/stylesheet.css b/src/css/stylesheet.css new file mode 100644 index 000000000..e26120e58 --- /dev/null +++ b/src/css/stylesheet.css @@ -0,0 +1,63 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + + :root { + --bg: 255 255 255; + --bg-active: 233 233 233; + --bg-secondary: 52 58 64; + --text-secondary: 128 128 128; + --text-secondary-active: 255 255 255; + --border: 212 212 212; + --text: 33 37 41; + --link: 0 123 255; + --link-active: 0 86 179; + --button-text: 255 255 255; + --button-text-secondary: 33 37 41; + --button-success: 8 119 21; + --button-success-active : 0 75 13; + --button-danger: 188 21 37; + --button-danger-active: 139 0 0; + --button-primary: 0 123 255; + --button-primary-active: 0 86 179; + --button-secondary: 108 117 125; + --button-secondary-active: 74 80 87; + --focus-ring: 3 102 214; + } + + :root[class~="dark"] { + --bg: 52 58 64; + --bg-active: 74 80 87; + --bg-secondary: 36 41 46; + --text-secondary: 128 128 128; + --text-secondary-active: 255 255 255; + --border: 37 42 46; + --text: 255 255 255; + --link: 50 166 255; + --link-active: 0 86 179; + --button-text: 255 255 255; + --button-text-secondary: 33 37 41; + --button-success: 8 119 21; + --button-success-active: 7 101 18; + --button-danger: 220 53 69; + --button-danger-active: 139 0 0; + --button-primary: 50 166 255; + --button-primary-active: 0 86 179; + --button-secondary: 108 117 125; + --button-secondary-active: 74 80 87; + --focus-ring: 3 102 214; + } +} + +.dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; +} diff --git a/src/datatypes/Notification.ts b/src/datatypes/Notification.ts new file mode 100644 index 000000000..71c7599de --- /dev/null +++ b/src/datatypes/Notification.ts @@ -0,0 +1,11 @@ +export interface Notification { + id: string; + date: number; + to: string; + from: string; + url?: string; + body: string; + status: 'r' | 'u'; + fromUsername?: string; + toStatusComp?: string; +} diff --git a/src/datatypes/User.ts b/src/datatypes/User.ts index ee6a6f666..dfca6d57b 100644 --- a/src/datatypes/User.ts +++ b/src/datatypes/User.ts @@ -1,5 +1,7 @@ import Cube from 'datatypes/Cube'; +import { Notification } from 'datatypes/Notification'; + export default interface User { id: string; username: string; @@ -15,4 +17,5 @@ export default interface User { theme?: string; hideFeatured?: boolean; patron?: string; + notifications?: Notification[]; } diff --git a/src/layouts/Footer.tsx b/src/layouts/Footer.tsx index ce2d4509d..f108a75ca 100644 --- a/src/layouts/Footer.tsx +++ b/src/layouts/Footer.tsx @@ -1,6 +1,4 @@ import React, { useEffect } from 'react'; -import { Col, Container, Row } from 'reactstrap'; - import Copyright from 'components/Copyright'; interface FooterLinkProps { @@ -9,7 +7,7 @@ interface FooterLinkProps { } const FooterLink: React.FC = ({ href, children }) => ( - + {children} ); @@ -22,88 +20,89 @@ const Footer: React.FC = () => { window['__cmp']('addConsentLink'); } }); + return ( -