diff --git a/commitlint.config.js b/commitlint.config.js index 3fd1fba..fbdae80 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -18,5 +18,13 @@ export default { 'revert', ], ], + + 'type-case': [2, 'always', 'lower-case'], + 'subject-empty': [2, 'never'], + 'subject-case': [ + 2, + 'never', + ['sentence-case', 'start-case', 'pascal-case', 'upper-case'], + ], }, -}; +}; \ No newline at end of file diff --git a/index.html b/index.html index 6821334..f30e919 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,8 @@ - ui-library + + Awesome UI Library
diff --git a/package-lock.json b/package-lock.json index f6da95a..df0e890 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "ui-library", "version": "0.0.0", "dependencies": { + "@floating-ui/react": "^0.27.19", "react": "^19.2.0", "react-dom": "^19.2.0" }, @@ -15,6 +16,9 @@ "@commitlint/cli": "^20.4.1", "@commitlint/config-conventional": "^20.4.1", "@eslint/js": "^9.39.1", + "@mdx-js/react": "^3.1.1", + "@mdx-js/rollup": "^3.1.1", + "@tailwindcss/typography": "^0.5.19", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", @@ -38,6 +42,7 @@ "playwright": "^1.58.2", "postcss": "^8.4.49", "prettier": "^3.8.1", + "react-router-dom": "^7.13.1", "tailwind-merge": "^3.4.0", "tailwindcss": "^3.4.17", "typescript": "~5.7.0", @@ -160,6 +165,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -808,6 +814,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -848,6 +855,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -1051,6 +1059,59 @@ } } }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.19", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz", + "integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.8", + "@floating-ui/utils": "^0.2.11", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1176,6 +1237,112 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@mdx-js/mdx/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", + "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@mdx-js/rollup": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/rollup/-/rollup-3.1.1.tgz", + "integrity": "sha512-v8satFmBB+DqDzYohnm1u2JOvxx6Hl3pUvqzJvfs2Zk/ngZ1aRUhsWpXvwPkNeGN9c2NCm/38H29ZqXQUjf8dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mdx-js/mdx": "^3.0.0", + "@rollup/pluginutils": "^5.0.0", + "source-map": "^0.7.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "rollup": ">=2" + } + }, + "node_modules/@mdx-js/rollup/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "node_modules/@microsoft/api-extractor": { "version": "7.56.3", "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.56.3.tgz", @@ -1912,6 +2079,33 @@ "dev": true, "license": "MIT" }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -2014,8 +2208,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -2073,6 +2266,16 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -2087,6 +2290,26 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2094,12 +2317,37 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.10.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.13.tgz", "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2110,6 +2358,7 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2120,10 +2369,18 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.55.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", @@ -2169,6 +2426,7 @@ "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", @@ -2380,6 +2638,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/@vitejs/plugin-react": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", @@ -2407,6 +2672,7 @@ "integrity": "sha512-gVQqh7paBz3gC+ZdcCmNSWJMk70IUjDeVqi+5m5vYpEHsIwRgw3Y545jljtajhkekIpIp5Gg8oK7bctgY0E2Ng==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/mocker": "4.0.18", "@vitest/utils": "4.0.18", @@ -2430,6 +2696,7 @@ "integrity": "sha512-gfajTHVCiwpxRj1qh0Sh/5bbGLG4F/ZH/V9xvFVoFddpITfMta9YGow0W6ZpTTORv2vdJuz9TnrNSmjKvpOf4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/browser": "4.0.18", "@vitest/mocker": "4.0.18", @@ -2574,6 +2841,7 @@ "integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/utils": "4.0.18", "fflate": "^0.8.2", @@ -2746,6 +3014,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3083,6 +3352,16 @@ "dev": true, "license": "MIT" }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "dev": true, + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -3166,6 +3445,17 @@ "node": ">= 0.4" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3250,6 +3540,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3355,6 +3646,17 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3372,6 +3674,50 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -3531,6 +3877,17 @@ "node": ">=6" } }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3558,6 +3915,17 @@ "dev": true, "license": "MIT" }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", @@ -3649,12 +4017,27 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -3896,6 +4279,20 @@ "dev": true, "license": "MIT" }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3949,6 +4346,20 @@ "node": ">=6" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -3978,8 +4389,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dot-prop": { "version": "5.3.0", @@ -4225,19 +4635,53 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", @@ -4254,6 +4698,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4486,6 +4931,114 @@ "node": ">=4.0" } }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -4527,6 +5080,13 @@ "dev": true, "license": "MIT" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5099,6 +5659,77 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -5278,6 +5909,13 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "dev": true, + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -5293,6 +5931,32 @@ "node": ">= 0.4" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -5448,6 +6112,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -5523,6 +6198,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -5847,6 +6533,7 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -6189,6 +6876,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -6205,7 +6903,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -6248,6 +6945,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6258,92 +6968,895 @@ "node": ">= 0.4" } }, - "node_modules/mdn-data": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", - "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 8" + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">=8.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8.6" + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=4" + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6695,6 +8208,33 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -6852,6 +8392,7 @@ "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright-core": "1.58.2" }, @@ -6918,6 +8459,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7093,7 +8635,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -7109,7 +8650,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -7117,6 +8657,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7170,6 +8721,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7179,6 +8731,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -7191,8 +8744,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-refresh": { "version": "0.18.0", @@ -7204,6 +8756,46 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", + "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", + "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-router": "7.13.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -7240,6 +8832,77 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -7311,6 +8974,72 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7403,6 +9132,7 @@ "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -7553,6 +9283,13 @@ "node": ">=10" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "dev": true, + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -7786,6 +9523,17 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -7932,6 +9680,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", @@ -7974,6 +9737,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/sucrase": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", @@ -8040,6 +9823,12 @@ "dev": true, "license": "MIT" }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, "node_modules/tailwind-merge": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", @@ -8057,6 +9846,7 @@ "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -8235,6 +10025,28 @@ "node": ">=20" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -8352,6 +10164,7 @@ "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8427,6 +10240,113 @@ "dev": true, "license": "MIT" }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -8485,12 +10405,43 @@ "dev": true, "license": "MIT" }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -9092,6 +11043,7 @@ "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", @@ -9600,6 +11552,7 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -9616,6 +11569,17 @@ "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 61c8274..fa01f39 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "private": true, "version": "0.0.0", "type": "module", + "sideEffects": [ + "*.css" + ], "scripts": { "dev": "vite", "build": "tsc -b && vite build", @@ -14,6 +17,7 @@ "test:coverage": "vitest run --coverage" }, "dependencies": { + "@floating-ui/react": "^0.27.19", "react": "^19.2.0", "react-dom": "^19.2.0" }, @@ -21,6 +25,9 @@ "@commitlint/cli": "^20.4.1", "@commitlint/config-conventional": "^20.4.1", "@eslint/js": "^9.39.1", + "@mdx-js/react": "^3.1.1", + "@mdx-js/rollup": "^3.1.1", + "@tailwindcss/typography": "^0.5.19", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", @@ -44,6 +51,7 @@ "playwright": "^1.58.2", "postcss": "^8.4.49", "prettier": "^3.8.1", + "react-router-dom": "^7.13.1", "tailwind-merge": "^3.4.0", "tailwindcss": "^3.4.17", "typescript": "~5.7.0", diff --git a/src/App.tsx b/src/App.tsx index 8d2452f..a8a5180 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,24 @@ +import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; import { ThemeProvider } from "@/theme"; import Playground from "./Playground"; +import DocsApp from "./docs/DocsApp"; import "@/theme/index.css"; function App() { return ( - + + + {/* Playground Route */} + } /> + + {/* Documentation Route */} + } /> + + {/* Fallback */} + } /> + + ); } diff --git a/src/Playground.tsx b/src/Playground.tsx index a83c306..c3f5343 100644 --- a/src/Playground.tsx +++ b/src/Playground.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { Link } from "react-router-dom"; import { Button } from "@/components/Button"; import { Input } from "@/components/Input"; import { Modal } from "@/components/Modal"; @@ -8,9 +9,12 @@ import { useTheme } from "@/theme"; import { Badge } from "./components/Badge"; import { Avatar } from "@/components/Avatar"; import { Skeleton } from "@/components/Skeleton"; -import Checkbox from "@/components/Checkbox"; +import { Checkbox } from "@/components/Checkbox"; import { ScrollArea, ScrollElement } from "@/components/ScrollArea"; -import RadioGroup from "@/components/RadioGroup"; +import { RadioGroup } from "@/components/RadioGroup"; +import Switch from "@/components/Switch/Switch"; +import Popover from "@/components/Popover/Popover"; +import { Dropdown } from "@/components/Dropdown/Dropdown"; import reactLogo from "@/assets/react.svg"; import type { ThemeOverride } from "@/theme"; @@ -167,6 +171,9 @@ export default function Playground() { const [isSimulatingData, setIsSimulatingData] = useState(true); const [radioValue, setRadioValue] = useState("option-1"); + const [dropdownValue, setDropdownValue] = useState(""); + const [paymentValue, setPaymentValue] = useState(""); + const [switchState, setSwitchState] = useState(false); const [checkedItems, setCheckedItems] = useState({ item1: false, @@ -219,6 +226,13 @@ export default function Playground() { Interactive playground for testing components +
+ + + +
@@ -1454,6 +1468,98 @@ export default function Playground() { + {/* Switch Section */} +
+
+ + Switches + + + Binary toggle controls with support for labels, loading states, and + different sizes. + +
+ +
+ {/* Basic Usage */} +
+
+ + Interactive State + +
+ + + + +
+
+
+ + {/* Sizes & States */} +
+
+ + Sizes & States + +
+ + + +
+
+ + +
+
+
+
+ + {/* Labels & Descriptions */} +
+ + Labels & Descriptions + +
+ + + +
+
+
+ {/* ScrollArea Section */}
+ {/* Popover Section */} +
+
+ + Popover + + + Smart, accessible floating popovers powered by @floating-ui/react. + +
+ +
+ + + Interactive Popover + + + This popover handles clicks naturally. Notice how focus is + trapped correctly. + +
+ +
+
+ } + > + + + + + + Aligned Top + + + Fades and shifts if screen is too small. + + + } + > + + + + +
+ + Real-World Use Cases + + +
+ {/* Real Case 1: Profile Menu */} +
+ + Profile Menu + + +
+ + John Doe + + + john.doe@enterprise.com + +
+ + + +
+ +
+ } + > +
+ +
+
+
+ + {/* Real Case 2: Advanced Filter */} +
+ + Advanced Filter / Settings + + +
+ + Dimensions Configuration + + + Set properties for the current view. + +
+ +
+ + + + +
+ +
+ + +
+
+ } + > + + +
+ + {/* Real Case 3: Interactive Info Card */} +
+ + System Info Tooltip + + +
+
+
+
+ + Systems Operational + + + All database clusters and API endpoints are currently + reporting 100% uptime in the US-East region. + + + View Detailed Status → + +
+
+ } + > + + +
+ + +
+ + {/* Dropdown Section */} +
+
+ + Dropdown + + + Accessible select menus with keyboard navigation, validation, sizes, + and loading states. + +
+ +
+ {/* Interactive */} +
+ + + Interactive + + +
+ setDropdownValue(val)} + /> + {dropdownValue && ( + + Selected: {dropdownValue} + + )} +
+ +
+ +
+ +
+ +
+ +
+ setPaymentValue(val)} + options={[ + { + label: "Credit Card", + value: "card", + icon: ( + + + + + ), + }, + { + label: "PayPal", + value: "paypal", + icon: ( + + + + ), + }, + { + label: "Bank Transfer", + value: "bank", + icon: ( + + + + + ), + }, + { + label: "Crypto", + value: "crypto", + icon: ( + + + + + + ), + disabled: true, + }, + ]} + /> +
+
+ + {/* States */} +
+ + + States + + + + + + + +
+ + {/* Sizes */} +
+ + + Sizes + + + + + + + +
+
+
+ {/* Modal */} { +export const CheckIcon = ({ + size, + className, +}: { + size?: string; + className?: string; +}) => { return ( { + return ( + + ); +}; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index c503789..6074c17 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -173,7 +173,7 @@ const Button = forwardRef( {isLoading ? ( <> - {children && {children}} + {children} ) : ( <> @@ -185,7 +185,7 @@ const Button = forwardRef( {prefixIcon} )} - {children && {children}} + {children} {suffixIcon && ( { diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 2233595..b0e2d29 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -1,7 +1,7 @@ import { forwardRef, useEffect, useRef, useImperativeHandle } from "react"; import type { CheckboxProps } from "./Checkbox.types"; import { useTheme } from "@/theme"; -import Skeleton from "../Skeleton"; +import { Skeleton } from "../Skeleton"; import { cn } from "@/utils/cn"; import { descriptionSizeClasses, @@ -111,7 +111,7 @@ const Checkbox = forwardRef(
( "peer-checked:[&_.check-icon]:opacity-100 peer-checked:[&_.check-icon]:scale-100", "peer-indeterminate:border-[var(--primary)] peer-indeterminate:bg-[var(--primary)]", "peer-indeterminate:[&_.indeterminate-icon]:opacity-100 peer-indeterminate:[&_.indeterminate-icon]:scale-100", - "peer-focus-visible:ring-2 peer-focus-visible:ring-[var(--ring)] peer-focus-visible:ring-offset-2", + "peer-focus-visible:ring-2 peer-focus-visible:ring-[var(--ring)]", )} > {/* Check Icon */} @@ -163,4 +163,4 @@ const Checkbox = forwardRef( Checkbox.displayName = "Checkbox"; -export default Checkbox; +export { Checkbox }; diff --git a/src/components/Checkbox/index.ts b/src/components/Checkbox/index.ts index dc638ad..e5bcad8 100644 --- a/src/components/Checkbox/index.ts +++ b/src/components/Checkbox/index.ts @@ -1,3 +1,3 @@ -export { default } from "./Checkbox"; +export { Checkbox } from "./Checkbox"; export * from "./Checkbox.types"; export * from "./Checkbox.constants"; diff --git a/src/components/Dropdown/Dropdown.constants.ts b/src/components/Dropdown/Dropdown.constants.ts new file mode 100644 index 0000000..fe6019d --- /dev/null +++ b/src/components/Dropdown/Dropdown.constants.ts @@ -0,0 +1,16 @@ +import type { DropdownProps } from "./Dropdown.types"; + +export const SKELETON_SIZE_MAP: Record< + NonNullable, + { height: string; width: string } +> = { + sm: { height: "28px", width: "100%" }, + md: { height: "32px", width: "100%" }, + lg: { height: "36px", width: "100%" }, +}; + +export const DROPDOWN_SIZE_MAP = { + sm: "h-8 px-2 text-xs", + md: "h-10 px-3 text-sm", + lg: "h-12 px-4 text-base", +}; diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx new file mode 100644 index 0000000..6e13244 --- /dev/null +++ b/src/components/Dropdown/Dropdown.tsx @@ -0,0 +1,521 @@ +import { + forwardRef, + useCallback, + useEffect, + useId, + useMemo, + useRef, + useState, +} from "react"; +import { + autoUpdate, + flip, + offset as offsetMiddleware, + size as sizeMiddleware, + useFloating, +} from "@floating-ui/react"; +import type { DropdownProps } from "./Dropdown.types"; +import { DROPDOWN_SIZE_MAP, SKELETON_SIZE_MAP } from "./Dropdown.constants"; +import { Skeleton } from "../Skeleton"; +import { useTheme } from "@/theme"; +import { cn } from "@/utils"; +import { Chevron } from "@/assets/Icons/Chevron"; +import { SearchIcon } from "@/assets/Icons/SearchIcon"; +import { Input } from "@/components/Input"; + +/* ─── Dropdown ─────────────────────────────────────────────────────────────── */ + +const Dropdown = forwardRef((props, ref) => { + const uid = useId(); + const { + id = uid, + options, + size = "md", + isLoading, + onChange, + label, + required = false, + selectedValue, + defaultOpen, + isOpen, + error, + disabled, + offset = 4, + className, + zIndex, + placeholder = "Select option", + searchable = false, + searchPlaceholder = "Search...", + } = props; + + const { theme } = useTheme(); + const isDark = theme.colorMode === "dark"; + + /* ── state ─────────────────────────────────────────────────────────────── */ + + const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false); + const [highlighted, setHighlighted] = useState(-1); + const [search, setSearch] = useState(""); + const triggerRef = useRef(null); + const panelRef = useRef(null); + const searchRef = useRef(null); + + const controlled = isOpen !== undefined; + const open = controlled ? isOpen : internalOpen; + + /* ── filtered options ────────────────────────────────────────────────────── */ + + const filtered = useMemo(() => { + if (!searchable || !search.trim()) return options; + const q = search.toLowerCase(); + return options.filter((o) => { + const text = typeof o.label === "string" ? o.label : String(o.label); + return text.toLowerCase().includes(q); + }); + }, [options, search, searchable]); + + /* ── floating-ui ───────────────────────────────────────────────────────── */ + + const { refs, floatingStyles } = useFloating({ + open, + placement: "bottom-start", + middleware: [ + offsetMiddleware(offset), + flip({ fallbackAxisSideDirection: "start" }), + sizeMiddleware({ + apply({ rects, elements }) { + Object.assign(elements.floating.style, { + width: `${rects.reference.width}px`, + maxWidth: `${rects.reference.width}px`, + }); + }, + }), + ], + whileElementsMounted: autoUpdate, + }); + + /* ── toggle / select ───────────────────────────────────────────────────── */ + + const toggle = useCallback( + (next: boolean) => { + if (disabled) return; + if (!controlled) setInternalOpen(next); + if (next) { + const idx = options.findIndex((o) => o.value === selectedValue); + setHighlighted(idx >= 0 ? idx : -1); + setSearch(""); + } else { + setHighlighted(-1); + setSearch(""); + } + }, + [disabled, controlled, options, selectedValue], + ); + + const select = useCallback( + (v: string) => { + if (disabled) return; + onChange?.(v); + toggle(false); + triggerRef.current?.focus(); + }, + [disabled, onChange, toggle], + ); + + /* ── side-effects ──────────────────────────────────────────────────────── */ + + // Focus search input when panel opens + useEffect(() => { + if (open && searchable) { + requestAnimationFrame(() => searchRef.current?.focus()); + } + }, [open, searchable]); + + // Close on outside click + useEffect(() => { + if (!open) return; + const handler = (e: PointerEvent) => { + const t = e.target as Node; + if (triggerRef.current?.contains(t) || panelRef.current?.contains(t)) + return; + toggle(false); + }; + document.addEventListener("pointerdown", handler); + return () => document.removeEventListener("pointerdown", handler); + }, [open, toggle]); + + // Scroll highlighted item into view + useEffect(() => { + if (!open || highlighted < 0 || !panelRef.current) return; + const items = + panelRef.current.querySelectorAll("button[data-item]"); + items[highlighted]?.scrollIntoView({ block: "nearest" }); + }, [highlighted, open]); + + /* ── keyboard ──────────────────────────────────────────────────────────── */ + + const step = useCallback( + (from: number, dir: 1 | -1) => { + const list = filtered; + let i = from; + for (const _ of list) { + void _; + i += dir; + if (i >= list.length) i = 0; + if (i < 0) i = list.length - 1; + if (!list[i]?.disabled) return i; + } + return from; + }, + [filtered], + ); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (disabled) return; + const list = filtered; + switch (e.key) { + case "ArrowDown": + e.preventDefault(); + if (!open) { + toggle(true); + setHighlighted( + Math.max( + list.findIndex((o) => o.value === selectedValue), + 0, + ), + ); + } else { + setHighlighted((p) => step(p, 1)); + } + break; + case "ArrowUp": + e.preventDefault(); + if (!open) { + toggle(true); + setHighlighted(list.length - 1); + } else { + setHighlighted((p) => step(p, -1)); + } + break; + case "Enter": + e.preventDefault(); + if (open && highlighted >= 0) { + const o = list[highlighted]; + if (o && !o.disabled) select(o.value); + } else if (!open) { + toggle(true); + } + break; + case " ": + // Allow space in search input + if (searchable && open) return; + e.preventDefault(); + if (open && highlighted >= 0) { + const o = list[highlighted]; + if (o && !o.disabled) select(o.value); + } else if (!open) { + toggle(true); + } + break; + case "Escape": + if (open) { + e.preventDefault(); + toggle(false); + triggerRef.current?.focus(); + } + break; + case "Home": + if (open) { + e.preventDefault(); + const f = list.findIndex((o) => !o.disabled); + if (f >= 0) setHighlighted(f); + } + break; + case "End": + if (open) { + e.preventDefault(); + const l = [...list].reverse().findIndex((o) => !o.disabled); + if (l >= 0) setHighlighted(list.length - 1 - l); + } + break; + } + }, + [ + disabled, + open, + filtered, + selectedValue, + highlighted, + toggle, + select, + step, + searchable, + ], + ); + + /* ── ref merging ───────────────────────────────────────────────────────── */ + + const setTrigger = useCallback( + (n: HTMLButtonElement | null) => { + triggerRef.current = n; + refs.setReference(n); + if (typeof ref === "function") ref(n); + else if (ref) ref.current = n; + }, + [ref, refs], + ); + + const setPanel = useCallback( + (n: HTMLDivElement | null) => { + panelRef.current = n; + refs.setFloating(n); + }, + [refs], + ); + + /* ── loading ───────────────────────────────────────────────────────────── */ + + if (isLoading) { + return ( + + ); + } + + /* ── derived ───────────────────────────────────────────────────────────── */ + + const chosen = options.find((o) => o.value === selectedValue); + const listboxId = `${id}-listbox`; + + const vars = { + "--dd-radius": theme.shape.radiusMd, + "--dd-radius-lg": theme.shape.radiusLg, + "--dd-border": error ? theme.palette.error[500] : theme.tokens.borderStrong, + "--dd-border-subtle": theme.tokens.borderSubtle, + "--dd-ring": error ? theme.palette.error[500] : theme.tokens.ring, + "--dd-bg": theme.tokens.input, + "--dd-bg-hover": theme.tokens.inputHover, + "--dd-text": theme.tokens.foreground, + "--dd-placeholder": theme.tokens.foregroundSubtle, + "--dd-label": theme.tokens.foreground, + "--dd-error": theme.palette.error[500], + "--dd-elevated": theme.tokens.elevated, + "--dd-muted": theme.tokens.foregroundMuted, + "--dd-accent": theme.tokens.accent, + "--dd-item-bg": `${theme.tokens.accent}18`, + "--dd-hover-bg": `${theme.tokens.foreground}0f`, + "--dd-panel-bg": theme.tokens.elevated, + "--dd-panel-border": theme.tokens.borderSubtle, + "--dd-panel-shadow": isDark + ? `0 8px 32px ${theme.neutral[50]}80` + : `0 8px 32px ${theme.neutral[400]}14`, + } as React.CSSProperties; + + /* ── render ────────────────────────────────────────────────────────────── */ + + return ( +
+ {label && ( + + )} + +
+ {/* ── trigger ──────────────────────────────────────────────────── */} + + + {/* ── panel ────────────────────────────────────────────────────── */} + {open && ( +
+
+ {/* ── search input ─────────────────────────────────────── */} + {searchable && ( +
+ { + const val = e.target.value; + setSearch(val); + const q = val.toLowerCase(); + const matches = q + ? options.filter((o) => { + const t = + typeof o.label === "string" + ? o.label + : String(o.label); + return t.toLowerCase().includes(q); + }) + : options; + setHighlighted(matches.length > 0 ? 0 : -1); + }} + onKeyDown={handleKeyDown} + placeholder={searchPlaceholder} + leftIcon={} + aria-label="Search options" + /> +
+ )} + + {/* ── options list ─────────────────────────────────────── */} +
+ {filtered.length === 0 ? ( +
+ {searchable && search + ? "No results found" + : "No options available"} +
+ ) : ( + filtered.map((option, i) => { + const active = option.value === selectedValue; + const lit = i === highlighted; + + return ( + + ); + }) + )} +
+
+
+ )} +
+ + {error && typeof error === "string" && ( + + {error} + + )} +
+ ); +}); + +Dropdown.displayName = "Dropdown"; + +export { Dropdown }; diff --git a/src/components/Dropdown/Dropdown.types.ts b/src/components/Dropdown/Dropdown.types.ts new file mode 100644 index 0000000..078c984 --- /dev/null +++ b/src/components/Dropdown/Dropdown.types.ts @@ -0,0 +1,60 @@ +export interface DropdownOption { + label: React.ReactNode; + value: string; + disabled?: boolean; + /** Icon element rendered before the label */ + icon?: React.ReactNode; +} + +export interface DropdownProps { + id?: string; + + /** Controlled open state */ + isOpen?: boolean; + + /** Uncontrolled initial state */ + defaultOpen?: boolean; + + /** Loading state */ + isLoading?: boolean; + + /** Size variant */ + size?: "sm" | "md" | "lg"; + + /** Value change handler */ + onChange?: (value: string) => void; + + label?: React.ReactNode; + + required?: boolean; + + /** Currently selected value */ + selectedValue?: string; + + /** List of options */ + options: DropdownOption[]; + + /** Error message */ + error?: string; + + /** Placeholder text when no option is selected */ + placeholder?: string; + + /** Disable dropdown */ + disabled?: boolean; + + /** Offset from trigger */ + offset?: number; + + /** Custom class */ + className?: string; + + /** z-index */ + zIndex?: number; + + /** Enable search/filter input inside the dropdown */ + searchable?: boolean; + + /** Placeholder for the search input */ + searchPlaceholder?: string; +} diff --git a/src/components/Input/Input.tsx b/src/components/Input/Input.tsx index f92357e..43bc8cd 100644 --- a/src/components/Input/Input.tsx +++ b/src/components/Input/Input.tsx @@ -80,7 +80,7 @@ const Input = forwardRef( className={cn( "group relative flex w-full items-center gap-2 px-3 rounded-[var(--radius)] border bg-[var(--bg)] transition-all duration-200", "border-[var(--border)]", - "focus-within:ring-2 focus-within:ring-[var(--ring)] focus-within:ring-offset-2", + "focus-within:ring-2 focus-within:ring-[var(--ring)]", disabled && "opacity-60 cursor-not-allowed", )} > diff --git a/src/components/Input/index.ts b/src/components/Input/index.ts index 35d2bdc..901daca 100644 --- a/src/components/Input/index.ts +++ b/src/components/Input/index.ts @@ -1 +1,2 @@ -export { Input, type InputProps } from './Input'; +export { Input } from "./Input"; +export type { InputProps } from "./Input.types"; diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 316fe47..1015da6 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -6,7 +6,7 @@ import type { ModalProps } from "./Modal.types"; import { MODAL_SIZES } from "./Modal.types"; import { CloseIcon } from "@/assets/Icons/CloseIcon"; -export const Modal: FC = ({ +const Modal: FC = ({ id, isOpen, onClose, @@ -108,4 +108,4 @@ export const Modal: FC = ({ Modal.displayName = "Modal"; -export default Modal; +export { Modal }; diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx new file mode 100644 index 0000000..92b12bf --- /dev/null +++ b/src/components/Popover/Popover.tsx @@ -0,0 +1,291 @@ +import React, { forwardRef, useState, useId, useCallback } from "react"; +import type { PopoverProps } from "./Popover.types"; +import { + useFloating, + autoUpdate, + offset as offsetMiddleware, + flip, + shift, + size as sizeMiddleware, + arrow as arrowMiddleware, + useClick, + useDismiss, + useRole, + useInteractions, + FloatingFocusManager, + useMergeRefs, + useTransitionStyles, + FloatingPortal, + FloatingArrow, +} from "@floating-ui/react"; +import { useRef } from "react"; +import { useTheme } from "@/theme"; +import { cn } from "@/utils"; + +const ARROW_HEIGHT = 8; +const ARROW_WIDTH = 8; +const ARROW_OFFSET = ARROW_HEIGHT; + +const Popover = forwardRef((props, ref) => { + const { theme } = useTheme(); + const generatedId = useId(); + + const { + children, + id = generatedId, + isOpen, + defaultOpen = false, + onOpenChange, + content, + placement = "bottom", + offset = 8, + showArrow = false, + disabled = false, + className, + zIndex = 1000, + usePortal = true, + portalContainer, + matchTriggerWidth = false, + keepTriggerFocus = false, + } = props; + + const [internalOpen, setInternalOpen] = useState(defaultOpen); + const isControlled = isOpen !== undefined; + const open = isControlled ? isOpen : internalOpen; + + const handleOpenChange = useCallback( + (newOpen: boolean) => { + if (disabled) return; + if (!isControlled) setInternalOpen(newOpen); + onOpenChange?.(newOpen); + }, + [disabled, isControlled, onOpenChange], + ); + + const arrowRef = useRef(null); + + const { + refs: floatingRefs, + floatingStyles, + context, + } = useFloating({ + open, + onOpenChange: handleOpenChange, + placement, + middleware: [ + offsetMiddleware(offset + (showArrow ? ARROW_OFFSET : 0)), + flip({ fallbackAxisSideDirection: "start" }), + shift({ padding: 8 }), + ...(matchTriggerWidth + ? [ + sizeMiddleware({ + apply({ rects, elements }) { + Object.assign(elements.floating.style, { + width: `${rects.reference.width}px`, + }); + }, + }), + ] + : []), + /* eslint-disable react-hooks/refs */ + ...(showArrow + ? [arrowMiddleware({ element: arrowRef, padding: 8 })] + : []), + /* eslint-enable react-hooks/refs */ + ], + whileElementsMounted: autoUpdate, + }); + + const { setReference, setFloating } = floatingRefs; + const mergedRef = useMergeRefs([setReference, ref]); + + const click = useClick(context, { + enabled: !disabled, + keyboardHandlers: !keepTriggerFocus, + }); + const dismiss = useDismiss(context, { outsidePressEvent: "mousedown" }); + const role = useRole(context, { + role: keepTriggerFocus ? "listbox" : "dialog", + }); + + const { getReferenceProps, getFloatingProps } = useInteractions([ + click, + dismiss, + role, + ]); + + const { isMounted, styles: transitionStyles } = useTransitionStyles(context, { + duration: { open: 180, close: 120 }, + initial: ({ side }) => ({ + opacity: 0, + transform: { + top: "scale(0.97) translateY(4px)", + bottom: "scale(0.97) translateY(-4px)", + left: "scale(0.97) translateX(4px)", + right: "scale(0.97) translateX(-4px)", + }[side], + }), + }); + + const arrowFill = theme.tokens.elevated; + + const isReactChild = React.isValidElement<{ + disabled?: boolean; + [key: string]: unknown; + }>(children); + + const referenceProps = getReferenceProps({ + id, + ...(isReactChild ? children.props : {}), + ...(isReactChild && { disabled: disabled || children.props.disabled }), + }); + + const trigger = isReactChild ? ( + // eslint-disable-next-line react-hooks/refs + React.cloneElement(children, { + ...referenceProps, + ref: mergedRef, + } as Partial<{ disabled?: boolean; [key: string]: unknown }> & { + ref: typeof mergedRef; + }) + ) : ( + + ); + + // ─── Render ────────────────────────────────────────────────────────────── + + const ContentWrapper = usePortal ? FloatingPortal : React.Fragment; + const portalProps = usePortal ? { root: portalContainer } : {}; + + return ( + <> + {trigger} + + {isMounted && ( + + {keepTriggerFocus ? ( +
+
+ {content} +
+ + {showArrow && ( + + )} +
+ ) : ( + +
+
+ {content} +
+ + {showArrow && ( + + )} +
+
+ )} +
+ )} + + ); +}); + +Popover.displayName = "Popover"; + +export default Popover; diff --git a/src/components/Popover/Popover.types.ts b/src/components/Popover/Popover.types.ts new file mode 100644 index 0000000..bd6345e --- /dev/null +++ b/src/components/Popover/Popover.types.ts @@ -0,0 +1,80 @@ +import type { Placement } from "@floating-ui/react"; + +export interface PopoverProps { + /** The trigger element. A valid React element gets cloneElement'd; raw text/nodes get wrapped in a + + {/* Label */} + {label && ( + + )} +
+ + {(description || helperText || error) && ( +
+ {error ? ( + + {error} + + ) : description || helperText ? ( + + {description || helperText} + + ) : null} +
+ )} + + ); +}); + +Switch.displayName = "Switch"; + +export default Switch; diff --git a/src/components/Switch/Switch.types.ts b/src/components/Switch/Switch.types.ts new file mode 100644 index 0000000..983e3d9 --- /dev/null +++ b/src/components/Switch/Switch.types.ts @@ -0,0 +1,42 @@ +export interface SwitchProps { + id?: string; + + /** Controlled state */ + checked?: boolean; + + /** Fired when switch changes */ + onChange?: (checked: boolean) => void; + + /** Disable interaction */ + disabled?: boolean; + + /** Required field */ + required?: boolean; + + /** Loading state */ + isLoading?: boolean; + + /** Switch size */ + size?: "small" | "medium" | "large"; + + /** Label next to switch */ + label?: React.ReactNode; + + /** description */ + description?: React.ReactNode; + + /** Helper text below */ + helperText?: React.ReactNode; + + /** Error message */ + error?: React.ReactNode; + + /** Name for form submission */ + name?: string; + + /** Custom class */ + className?: string; + + /** Inline style */ + style?: React.CSSProperties; +} diff --git a/src/components/Switch/index.ts b/src/components/Switch/index.ts new file mode 100644 index 0000000..5d9be1a --- /dev/null +++ b/src/components/Switch/index.ts @@ -0,0 +1,2 @@ +export { default as Switch } from "./Switch"; +export * from "./Switch.types"; diff --git a/src/components/index.ts b/src/components/index.ts index e95ab28..b3e5f72 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -9,6 +9,7 @@ export * from "./Skeleton"; export * from "./Checkbox"; export * from "./ScrollArea"; export * from "./RadioGroup"; +export * from "./Switch"; // ── Theme ── export { ThemeProvider, useTheme, defaultTheme, darkTheme } from "../theme"; diff --git a/src/docs/DocsApp.tsx b/src/docs/DocsApp.tsx new file mode 100644 index 0000000..4ab5521 --- /dev/null +++ b/src/docs/DocsApp.tsx @@ -0,0 +1,1173 @@ +import React, { Suspense, lazy, useState } from "react"; +import { Routes, Route, Navigate, Link } from "react-router-dom"; +import { DocsLayout } from "./DocsLayout"; +import { useTheme } from "@/theme"; +import { Button } from "@/components/Button"; +import { Input } from "@/components/Input"; +import { Badge } from "@/components/Badge"; +import { Checkbox } from "@/components/Checkbox"; + +// Lazy load MDX pages +const ButtonDocs = lazy(() => import("./pages/Button.mdx")); +const InputDocs = lazy(() => import("./pages/Input.mdx")); +const RadioGroupDocs = lazy(() => import("./pages/RadioGroup.mdx")); +const AccordionDocs = lazy(() => import("./pages/Accordion.mdx")); +const AvatarDocs = lazy(() => import("./pages/Avatar.mdx")); +const BadgeDocs = lazy(() => import("./pages/Badge.mdx")); +const CheckboxDocs = lazy(() => import("./pages/Checkbox.mdx")); +const DropdownDocs = lazy(() => import("./pages/Dropdown.mdx")); +const ModalDocs = lazy(() => import("./pages/Modal.mdx")); +const PopoverDocs = lazy(() => import("./pages/Popover.mdx")); +const ScrollAreaDocs = lazy(() => import("./pages/ScrollArea.mdx")); +const SkeletonDocs = lazy(() => import("./pages/Skeleton.mdx")); +const SwitchDocs = lazy(() => import("./pages/Switch.mdx")); +const TypographyDocs = lazy(() => import("./pages/Typography.mdx")); +const ThemingPage = lazy(() => + import("./pages/ThemingPage").then((m) => ({ default: m.ThemingPage })), +); + +const components = [ + { + name: "Accordion", + path: "/docs/accordion", + description: "Collapsible content sections", + }, + { + name: "Avatar", + path: "/docs/avatar", + description: "User profile images with fallbacks", + }, + { + name: "Badge", + path: "/docs/badge", + description: "Status indicators and labels", + }, + { + name: "Button", + path: "/docs/button", + description: "Interactive action triggers", + }, + { + name: "Checkbox", + path: "/docs/checkbox", + description: "Multi-select form controls", + }, + { + name: "Dropdown", + path: "/docs/dropdown", + description: "Select menus with keyboard navigation", + }, + { + name: "Input", + path: "/docs/input", + description: "Text fields with validation", + }, + { + name: "Modal", + path: "/docs/modal", + description: "Accessible dialog overlays", + }, + { + name: "Popover", + path: "/docs/popover", + description: "Contextual floating menus", + }, + { + name: "Radio Group", + path: "/docs/radio-group", + description: "Single-select option groups", + }, + { + name: "Scroll Area", + path: "/docs/scroll-area", + description: "Scrollable content containers", + }, + { + name: "Skeleton", + path: "/docs/skeleton", + description: "Loading placeholders", + }, + { + name: "Switch", + path: "/docs/switch", + description: "Instant setting toggles", + }, + { + name: "Typography", + path: "/docs/typography", + description: "Text styles and hierarchy", + }, +]; + +const COLOR_PRESETS = [ + { name: "Pink", value: "#ec4899" }, + { name: "Blue", value: "#3b82f6" }, + { name: "Violet", value: "#8b5cf6" }, + { name: "Emerald", value: "#10b981" }, + { name: "Orange", value: "#f97316" }, + { name: "Red", value: "#ef4444" }, + { name: "Cyan", value: "#06b6d4" }, + { name: "Rose", value: "#f43f5e" }, +]; + +function hexToHSL(hex: string): [number, number, number] { + const r = parseInt(hex.slice(1, 3), 16) / 255; + const g = parseInt(hex.slice(3, 5), 16) / 255; + const b = parseInt(hex.slice(5, 7), 16) / 255; + const max = Math.max(r, g, b), + min = Math.min(r, g, b); + const l = (max + min) / 2; + if (max === min) return [0, 0, l]; + const d = max - min; + const s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + let h = 0; + if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6; + else if (max === g) h = ((b - r) / d + 2) / 6; + else h = ((r - g) / d + 4) / 6; + return [h * 360, s * 100, l * 100]; +} + +function hslToHex(h: number, s: number, l: number): string { + s /= 100; + l /= 100; + const a = s * Math.min(l, 1 - l); + const f = (n: number) => { + const k = (n + h / 30) % 12; + const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); + return Math.round(255 * Math.max(0, Math.min(1, color))) + .toString(16) + .padStart(2, "0"); + }; + return `#${f(0)}${f(8)}${f(4)}`; +} + +function generatePaletteFromColor(hex: string) { + const [h, s] = hexToHSL(hex); + return { + 50: hslToHex(h, Math.min(100, s * 1.1), 97), + 100: hslToHex(h, Math.min(100, s * 1.1), 93), + 200: hslToHex(h, Math.min(100, s), 85), + 300: hslToHex(h, Math.min(100, s * 0.95), 73), + 400: hslToHex(h, Math.min(100, s * 0.9), 60), + 500: hex, + 600: hslToHex(h, Math.min(100, s * 0.95), 42), + 700: hslToHex(h, Math.min(100, s * 0.9), 34), + 800: hslToHex(h, Math.min(100, s * 0.85), 26), + 900: hslToHex(h, Math.min(100, s * 0.8), 18), + }; +} + +// ─── Surface Theme Presets ───────────────────────────────────────────────── + +interface SurfacePreset { + name: string; + description: string; + preview: [string, string, string]; + neutral: Record; + tokens: Record; +} + +const DARK_SURFACE_PRESETS: SurfacePreset[] = [ + { + name: "Midnight", + description: "Pure black", + preview: ["#080808", "#181818", "#f2f2f2"], + neutral: { + 50: "#080808", + 100: "#101010", + 200: "#181818", + 300: "#202020", + 400: "#383838", + 500: "#585858", + 600: "#787878", + 700: "#a0a0a0", + 800: "#c8c8c8", + 900: "#f2f2f2", + }, + tokens: { + background: "#080808", + surface: "#101010", + surfaceHover: "#181818", + elevated: "#181818", + foreground: "#f2f2f2", + foregroundMuted: "#a0a0a0", + foregroundSubtle: "#787878", + border: "#202020", + borderSubtle: "#181818", + borderStrong: "#383838", + input: "#181818", + inputHover: "#202020", + inputFocus: "#101010", + }, + }, + { + name: "Charcoal", + description: "Soft dark gray", + preview: ["#121212", "#1e1e1e", "#e8e8e8"], + neutral: { + 50: "#121212", + 100: "#1a1a1a", + 200: "#1e1e1e", + 300: "#2c2c2c", + 400: "#404040", + 500: "#5e5e5e", + 600: "#808080", + 700: "#a8a8a8", + 800: "#d0d0d0", + 900: "#e8e8e8", + }, + tokens: { + background: "#121212", + surface: "#1a1a1a", + surfaceHover: "#1e1e1e", + elevated: "#1e1e1e", + foreground: "#e8e8e8", + foregroundMuted: "#a8a8a8", + foregroundSubtle: "#808080", + border: "#2c2c2c", + borderSubtle: "#1e1e1e", + borderStrong: "#404040", + input: "#1e1e1e", + inputHover: "#2c2c2c", + inputFocus: "#1a1a1a", + }, + }, + { + name: "Ocean", + description: "Deep blue", + preview: ["#0a1628", "#111d33", "#e2e8f0"], + neutral: { + 50: "#0a1628", + 100: "#0f1d2e", + 200: "#111d33", + 300: "#1a2a42", + 400: "#2a3f5f", + 500: "#4a6182", + 600: "#6b82a0", + 700: "#8fa3bb", + 800: "#c4d1de", + 900: "#e2e8f0", + }, + tokens: { + background: "#0a1628", + surface: "#0f1d2e", + surfaceHover: "#111d33", + elevated: "#111d33", + foreground: "#e2e8f0", + foregroundMuted: "#8fa3bb", + foregroundSubtle: "#6b82a0", + border: "#1a2a42", + borderSubtle: "#111d33", + borderStrong: "#2a3f5f", + input: "#111d33", + inputHover: "#1a2a42", + inputFocus: "#0f1d2e", + }, + }, + { + name: "Glass Blue", + description: "Frosted blue glass", + preview: ["#0c1929", "#132640", "#dbeafe"], + neutral: { + 50: "#0c1929", + 100: "#102030", + 200: "#132640", + 300: "#1c3554", + 400: "#2b4d75", + 500: "#3e6a9a", + 600: "#5b8aba", + 700: "#8cb4da", + 800: "#bcd5f0", + 900: "#dbeafe", + }, + tokens: { + background: "#0c1929", + surface: "#102030", + surfaceHover: "#162d48", + elevated: "#132640", + foreground: "#dbeafe", + foregroundMuted: "#8cb4da", + foregroundSubtle: "#5b8aba", + border: "#1e3a5c", + borderSubtle: "#162d48", + borderStrong: "#2b4d75", + input: "#132640", + inputHover: "#1c3554", + inputFocus: "#102030", + }, + }, + { + name: "Slate", + description: "Cool blue-gray", + preview: ["#0f172a", "#1e293b", "#f1f5f9"], + neutral: { + 50: "#0f172a", + 100: "#1e293b", + 200: "#1e293b", + 300: "#334155", + 400: "#475569", + 500: "#64748b", + 600: "#94a3b8", + 700: "#cbd5e1", + 800: "#e2e8f0", + 900: "#f1f5f9", + }, + tokens: { + background: "#0f172a", + surface: "#1e293b", + surfaceHover: "#273549", + elevated: "#1e293b", + foreground: "#f1f5f9", + foregroundMuted: "#94a3b8", + foregroundSubtle: "#64748b", + border: "#334155", + borderSubtle: "#1e293b", + borderStrong: "#475569", + input: "#1e293b", + inputHover: "#334155", + inputFocus: "#1e293b", + }, + }, + { + name: "Violet", + description: "Purple tint", + preview: ["#0e0a1a", "#1a1425", "#ede9fe"], + neutral: { + 50: "#0e0a1a", + 100: "#141020", + 200: "#1a1425", + 300: "#251e35", + 400: "#3b3255", + 500: "#5b5278", + 600: "#7c739a", + 700: "#a09abc", + 800: "#cbc5de", + 900: "#ede9fe", + }, + tokens: { + background: "#0e0a1a", + surface: "#141020", + surfaceHover: "#1a1425", + elevated: "#1a1425", + foreground: "#ede9fe", + foregroundMuted: "#a09abc", + foregroundSubtle: "#7c739a", + border: "#251e35", + borderSubtle: "#1a1425", + borderStrong: "#3b3255", + input: "#1a1425", + inputHover: "#251e35", + inputFocus: "#141020", + }, + }, + { + name: "Rose Dark", + description: "Warm rose tint", + preview: ["#1a0a10", "#261218", "#fce7f3"], + neutral: { + 50: "#1a0a10", + 100: "#201015", + 200: "#261218", + 300: "#3a1c25", + 400: "#552a38", + 500: "#7a4055", + 600: "#a06078", + 700: "#c08898", + 800: "#dbb0be", + 900: "#fce7f3", + }, + tokens: { + background: "#1a0a10", + surface: "#201015", + surfaceHover: "#261218", + elevated: "#261218", + foreground: "#fce7f3", + foregroundMuted: "#c08898", + foregroundSubtle: "#a06078", + border: "#3a1c25", + borderSubtle: "#261218", + borderStrong: "#552a38", + input: "#261218", + inputHover: "#3a1c25", + inputFocus: "#201015", + }, + }, + { + name: "Forest", + description: "Deep green", + preview: ["#071210", "#0d1f1b", "#ecfdf5"], + neutral: { + 50: "#071210", + 100: "#0a1a17", + 200: "#0d1f1b", + 300: "#162e28", + 400: "#264a40", + 500: "#3d6b5e", + 600: "#5a8c7c", + 700: "#86b5a2", + 800: "#b8d8ca", + 900: "#ecfdf5", + }, + tokens: { + background: "#071210", + surface: "#0a1a17", + surfaceHover: "#0d1f1b", + elevated: "#0d1f1b", + foreground: "#ecfdf5", + foregroundMuted: "#86b5a2", + foregroundSubtle: "#5a8c7c", + border: "#162e28", + borderSubtle: "#0d1f1b", + borderStrong: "#264a40", + input: "#0d1f1b", + inputHover: "#162e28", + inputFocus: "#0a1a17", + }, + }, + { + name: "Warm", + description: "Cozy brown", + preview: ["#120f0b", "#1c1814", "#faf5ef"], + neutral: { + 50: "#120f0b", + 100: "#181510", + 200: "#1c1814", + 300: "#2a2520", + 400: "#3e3830", + 500: "#5c5448", + 600: "#7a7165", + 700: "#a09688", + 800: "#c8bfb2", + 900: "#faf5ef", + }, + tokens: { + background: "#120f0b", + surface: "#181510", + surfaceHover: "#1c1814", + elevated: "#1c1814", + foreground: "#faf5ef", + foregroundMuted: "#a09688", + foregroundSubtle: "#7a7165", + border: "#2a2520", + borderSubtle: "#1c1814", + borderStrong: "#3e3830", + input: "#1c1814", + inputHover: "#2a2520", + inputFocus: "#181510", + }, + }, + { + name: "Onyx", + description: "True black OLED", + preview: ["#000000", "#0a0a0a", "#fafafa"], + neutral: { + 50: "#000000", + 100: "#050505", + 200: "#0a0a0a", + 300: "#151515", + 400: "#2a2a2a", + 500: "#505050", + 600: "#707070", + 700: "#999999", + 800: "#c0c0c0", + 900: "#fafafa", + }, + tokens: { + background: "#000000", + surface: "#050505", + surfaceHover: "#0a0a0a", + elevated: "#0a0a0a", + foreground: "#fafafa", + foregroundMuted: "#999999", + foregroundSubtle: "#707070", + border: "#151515", + borderSubtle: "#0a0a0a", + borderStrong: "#2a2a2a", + input: "#0a0a0a", + inputHover: "#151515", + inputFocus: "#050505", + }, + }, +]; + +const LIGHT_SURFACE_PRESETS: SurfacePreset[] = [ + { + name: "Default", + description: "Clean white", + preview: ["#fbfaf9", "#ffffff", "#111827"], + neutral: { + 50: "#fbfaf9", + 100: "#f3f4f6", + 200: "#e5e7eb", + 300: "#d1d5db", + 400: "#9ca3af", + 500: "#6b7280", + 600: "#4b5563", + 700: "#374151", + 800: "#1f2937", + 900: "#111827", + }, + tokens: { + background: "#fbfaf9", + surface: "#ffffff", + surfaceHover: "#f3f4f6", + elevated: "#ffffff", + foreground: "#111827", + foregroundMuted: "#6b7280", + foregroundSubtle: "#9ca3af", + border: "#e5e7eb", + borderSubtle: "#f3f4f6", + borderStrong: "#9ca3af", + input: "#ffffff", + inputHover: "#fbfaf9", + inputFocus: "#ffffff", + }, + }, + { + name: "Snow Blue", + description: "Cool blue tint", + preview: ["#f0f4f8", "#ffffff", "#1a2b4a"], + neutral: { + 50: "#f0f4f8", + 100: "#e8edf2", + 200: "#d5dce6", + 300: "#b8c4d4", + 400: "#8a9ab5", + 500: "#5d7096", + 600: "#445a7a", + 700: "#344763", + 800: "#25344c", + 900: "#1a2b4a", + }, + tokens: { + background: "#f0f4f8", + surface: "#ffffff", + surfaceHover: "#e8edf2", + elevated: "#ffffff", + foreground: "#1a2b4a", + foregroundMuted: "#5d7096", + foregroundSubtle: "#8a9ab5", + border: "#d5dce6", + borderSubtle: "#e8edf2", + borderStrong: "#8a9ab5", + input: "#ffffff", + inputHover: "#f0f4f8", + inputFocus: "#ffffff", + }, + }, + { + name: "Warm Sand", + description: "Soft cream", + preview: ["#faf6f0", "#ffffff", "#2d1f10"], + neutral: { + 50: "#faf6f0", + 100: "#f3ede4", + 200: "#e5ddd0", + 300: "#d1c5b4", + 400: "#a89680", + 500: "#7a6b58", + 600: "#5c4f3e", + 700: "#443a2e", + 800: "#322b20", + 900: "#2d1f10", + }, + tokens: { + background: "#faf6f0", + surface: "#ffffff", + surfaceHover: "#f3ede4", + elevated: "#ffffff", + foreground: "#2d1f10", + foregroundMuted: "#7a6b58", + foregroundSubtle: "#a89680", + border: "#e5ddd0", + borderSubtle: "#f3ede4", + borderStrong: "#a89680", + input: "#ffffff", + inputHover: "#faf6f0", + inputFocus: "#ffffff", + }, + }, + { + name: "Lavender", + description: "Soft purple tint", + preview: ["#f5f3ff", "#ffffff", "#1e1040"], + neutral: { + 50: "#f5f3ff", + 100: "#ede9fe", + 200: "#ddd6fe", + 300: "#c4b5fd", + 400: "#a78bfa", + 500: "#7c5cbf", + 600: "#5b4399", + 700: "#453378", + 800: "#2e2254", + 900: "#1e1040", + }, + tokens: { + background: "#f5f3ff", + surface: "#ffffff", + surfaceHover: "#ede9fe", + elevated: "#ffffff", + foreground: "#1e1040", + foregroundMuted: "#5b4399", + foregroundSubtle: "#a78bfa", + border: "#ddd6fe", + borderSubtle: "#ede9fe", + borderStrong: "#a78bfa", + input: "#ffffff", + inputHover: "#f5f3ff", + inputFocus: "#ffffff", + }, + }, + { + name: "Mint", + description: "Fresh green tint", + preview: ["#f0fdf4", "#ffffff", "#052e16"], + neutral: { + 50: "#f0fdf4", + 100: "#dcfce7", + 200: "#bbf7d0", + 300: "#86efac", + 400: "#4ade80", + 500: "#22c55e", + 600: "#16a34a", + 700: "#15803d", + 800: "#166534", + 900: "#052e16", + }, + tokens: { + background: "#f0fdf4", + surface: "#ffffff", + surfaceHover: "#dcfce7", + elevated: "#ffffff", + foreground: "#052e16", + foregroundMuted: "#16a34a", + foregroundSubtle: "#4ade80", + border: "#bbf7d0", + borderSubtle: "#dcfce7", + borderStrong: "#4ade80", + input: "#ffffff", + inputHover: "#f0fdf4", + inputFocus: "#ffffff", + }, + }, + { + name: "Rose", + description: "Warm pink tint", + preview: ["#fff1f2", "#ffffff", "#4c0519"], + neutral: { + 50: "#fff1f2", + 100: "#ffe4e6", + 200: "#fecdd3", + 300: "#fda4af", + 400: "#fb7185", + 500: "#f43f5e", + 600: "#e11d48", + 700: "#be123c", + 800: "#9f1239", + 900: "#4c0519", + }, + tokens: { + background: "#fff1f2", + surface: "#ffffff", + surfaceHover: "#ffe4e6", + elevated: "#ffffff", + foreground: "#4c0519", + foregroundMuted: "#be123c", + foregroundSubtle: "#fb7185", + border: "#fecdd3", + borderSubtle: "#ffe4e6", + borderStrong: "#fb7185", + input: "#ffffff", + inputHover: "#fff1f2", + inputFocus: "#ffffff", + }, + }, +]; + +const CopyInstallButton: React.FC = () => { + const { theme } = useTheme(); + const [copied, setCopied] = useState(false); + return ( + + ); +}; + +const ColorPicker: React.FC = () => { + const { theme, overrideTheme, resetTheme } = useTheme(); + const [customColor, setCustomColor] = useState(theme.palette.primary[500]); + const [copied, setCopied] = useState(null); + + const applyColor = (color: string) => { + setCustomColor(color); + const palette = generatePaletteFromColor(color); + overrideTheme({ + palette: { primary: palette }, + tokens: { + accent: palette[600], + accentForeground: "#ffffff", + accentHover: palette[700], + ring: palette[500], + }, + }); + }; + + const copyColor = (value: string, label: string) => { + navigator.clipboard.writeText(value); + setCopied(label); + setTimeout(() => setCopied(null), 1500); + }; + + return ( +
+
+

Primary Color

+ +
+ + {/* Preset swatches */} +
+ {COLOR_PRESETS.map((preset) => ( +
+ + {/* Custom color input + copy */} +
+ applyColor(e.target.value)} + className="w-8 h-8 rounded cursor-pointer border-0 p-0" + /> + { + const v = e.target.value; + if (/^#[0-9a-fA-F]{6}$/.test(v)) applyColor(v); + setCustomColor(v); + }} + className="h-8 px-3 rounded-md border text-sm font-mono w-28" + style={{ + borderColor: theme.tokens.border, + backgroundColor: theme.tokens.background, + color: theme.tokens.foreground, + }} + maxLength={7} + /> + +
+ + {/* Theme Variations */} +
+
+ {theme.colorMode === "dark" ? "Dark" : "Light"} Surface Themes +
+
+ {(theme.colorMode === "dark" + ? DARK_SURFACE_PRESETS + : LIGHT_SURFACE_PRESETS + ).map((preset) => ( + + ))} +
+
+
+ ); +}; + +const LivePreview: React.FC = () => { + const { theme } = useTheme(); + + return ( +
+

Live Preview

+
+ + + + +
+ +
+ Active + Online + Pending + Offline +
+ +
+ ); +}; + +const LandingPage: React.FC = () => { + const { theme } = useTheme(); + + return ( +
+ {/* Hero + Theme Customizer */} +
+
+ v2.0.0 +
+

+ Build better interfaces,{" "} + faster. +

+

+ A production-ready React component library with built-in theming, + accessibility, and TypeScript support. +

+
+ + + + +
+
+ + {/* Theme Customizer - visible without scrolling */} +
+

+ Pick a primary color to preview +

+
+
+ + +
+ + {/* Features */} +
+

+ Why Awesome UI? +

+

+ Built for teams that ship fast without cutting corners. +

+
+ {[ + { + icon: ( + + + + ), + title: "Accessible", + desc: "WAI-ARIA compliant. Full keyboard navigation and screen reader support out of the box.", + }, + { + icon: ( + + + + ), + title: "Themeable", + desc: "CSS variable theming with light/dark modes. Override any color at runtime — zero JS cost.", + }, + { + icon: ( + + + + ), + title: "TypeScript", + desc: "Every component is fully typed. Exported interfaces for all props — autocomplete just works.", + }, + ].map((feature) => ( +
+
+ {feature.icon} +
+

{feature.title}

+

+ {feature.desc} +

+
+ ))} +
+
+ + {/* Components Grid */} +
+
+

Components

+ + {components.length} available + +
+
+ {components.map((comp) => ( + { + e.currentTarget.style.borderColor = theme.palette.primary[500]; + }} + onMouseLeave={(e) => { + e.currentTarget.style.borderColor = theme.tokens.border; + }} + > +
{comp.name}
+
+ {comp.description} +
+ + ))} +
+
+
+ ); +}; + +export const DocsApp: React.FC = () => { + return ( + + + Loading... + + } + > + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + ); +}; + +export default DocsApp; diff --git a/src/docs/DocsLayout.tsx b/src/docs/DocsLayout.tsx new file mode 100644 index 0000000..19aec0c --- /dev/null +++ b/src/docs/DocsLayout.tsx @@ -0,0 +1,391 @@ +import React, { useState, useMemo } from "react"; +import { Link, useLocation } from "react-router-dom"; +import { cn } from "@/utils"; +import { useTheme } from "@/theme"; + +const navigation = [ + { + title: "Getting Started", + links: [ + { name: "Introduction", path: "/docs" }, + { name: "Theming", path: "/docs/theming" }, + ], + }, + { + title: "Components", + links: [ + { name: "Accordion", path: "/docs/accordion" }, + { name: "Avatar", path: "/docs/avatar" }, + { name: "Badge", path: "/docs/badge" }, + { name: "Button", path: "/docs/button" }, + { name: "Checkbox", path: "/docs/checkbox" }, + { name: "Dropdown", path: "/docs/dropdown" }, + { name: "Input", path: "/docs/input" }, + { name: "Modal", path: "/docs/modal" }, + { name: "Popover", path: "/docs/popover" }, + { name: "Radio Group", path: "/docs/radio-group" }, + { name: "Scroll Area", path: "/docs/scroll-area" }, + { name: "Skeleton", path: "/docs/skeleton" }, + { name: "Switch", path: "/docs/switch" }, + { name: "Typography", path: "/docs/typography" }, + ], + }, +]; + +export const DocsLayout: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const location = useLocation(); + const { theme, toggleColorMode } = useTheme(); + const [isSidebarOpen, setIsSidebarOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + + const activeLink = navigation + .flatMap((n) => n.links) + .find((l) => l.path === location.pathname); + + const isHomePage = + location.pathname === "/docs" || location.pathname === "/docs/"; + + const filteredNavigation = useMemo(() => { + if (!searchQuery) return navigation; + return navigation + .map((section) => ({ + ...section, + links: section.links.filter((link) => + link.name.toLowerCase().includes(searchQuery.toLowerCase()), + ), + })) + .filter((section) => section.links.length > 0); + }, [searchQuery]); + + return ( +
+ {/* Header */} +
+
+ +
+ UI +
+ Awesome UI + + + +
+ +
+ {/* Search */} +
+ setSearchQuery(e.target.value)} + className="h-8 px-3 pl-8 rounded-md border text-sm w-52 focus:w-64 transition-all outline-none focus:ring-1" + style={{ + borderColor: theme.tokens.border, + backgroundColor: theme.tokens.surface, + color: theme.tokens.foreground, + }} + onFocus={(e) => { + e.target.style.borderColor = theme.palette.primary[500]; + }} + onBlur={(e) => { + e.target.style.borderColor = theme.tokens.border; + }} + /> + + + + {searchQuery && ( + + )} +
+ + {/* Dark mode toggle */} + + + + + + + {/* Mobile menu */} + +
+
+ +
+ {/* Sidebar */} + + + {/* Main Content */} +
+ {/* Breadcrumb */} + {!isHomePage && ( + + )} + +
+ {children} +
+
+
+ + {/* Footer */} + {isHomePage && ( +
+ Built with React, TypeScript & Tailwind CSS +
+ )} +
+ ); +}; diff --git a/src/docs/components/ComponentPreview.tsx b/src/docs/components/ComponentPreview.tsx new file mode 100644 index 0000000..e1aa054 --- /dev/null +++ b/src/docs/components/ComponentPreview.tsx @@ -0,0 +1,94 @@ +import React, { useState } from "react"; +import { useTheme } from "@/theme"; + +interface ComponentPreviewProps { + children: React.ReactNode; + code: string; + className?: string; +} + +export const ComponentPreview: React.FC = ({ + children, + code, + className, +}) => { + const [showCode, setShowCode] = useState(false); + const [copied, setCopied] = useState(false); + const { theme } = useTheme(); + + const copyToClipboard = () => { + navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+ {/* Preview */} +
+
{children}
+
+ + {/* Actions */} +
+ + +
+ + {/* Code */} + {showCode && ( +
+
+            {code}
+          
+
+ )} +
+ ); +}; diff --git a/src/docs/components/DropdownDemo.tsx b/src/docs/components/DropdownDemo.tsx new file mode 100644 index 0000000..2deb4e3 --- /dev/null +++ b/src/docs/components/DropdownDemo.tsx @@ -0,0 +1,234 @@ +import React, { useState } from "react"; +import { Dropdown } from "@/components/Dropdown/Dropdown"; +import { Badge } from "@/components/Badge"; + +export const BasicDemo: React.FC = () => { + const [value, setValue] = useState(""); + + return ( +
+ + {value && ( + + Selected: {value} + + )} +
+ ); +}; + +export const IconDemo: React.FC = () => { + const [value, setValue] = useState(""); + + return ( +
+ + + + + ), + }, + { + label: "Bank Transfer", + value: "bank", + icon: ( + + + + + ), + }, + { + label: "Crypto", + value: "crypto", + icon: ( + + + + + + ), + disabled: true, + }, + ]} + /> +
+ ); +}; + +const COUNTRIES = [ + "United States", + "United Kingdom", + "Canada", + "Germany", + "France", + "Japan", + "Australia", + "Brazil", + "India", + "South Korea", + "Mexico", + "Italy", + "Spain", + "Netherlands", + "Sweden", + "Norway", + "Switzerland", + "Singapore", + "New Zealand", + "Argentina", +]; + +export const ScrollDemo: React.FC = () => { + const [value, setValue] = useState(""); + + return ( +
+ ({ + label: c, + value: c.toLowerCase().replace(/\s+/g, "-"), + }))} + /> +
+ ); +}; + +export const SearchDemo: React.FC = () => { + const [value, setValue] = useState(""); + + return ( +
+ ({ + label: c, + value: c.toLowerCase().replace(/\s+/g, "-"), + }))} + /> +
+ ); +}; + +export const SizesDemo: React.FC = () => ( +
+
+ +
+
+ +
+
+ +
+
+); + +export const StatesDemo: React.FC = () => ( +
+ + + +
+); diff --git a/src/docs/components/InfiniteScrollDemo.tsx b/src/docs/components/InfiniteScrollDemo.tsx new file mode 100644 index 0000000..8203269 --- /dev/null +++ b/src/docs/components/InfiniteScrollDemo.tsx @@ -0,0 +1,101 @@ +import React, { useState, useCallback } from "react"; +import { ScrollArea, ScrollElement } from "@/components/ScrollArea"; +import { useTheme } from "@/theme"; + +export const InfiniteScrollDemo: React.FC = () => { + const { theme } = useTheme(); + const [items, setItems] = useState( + Array.from({ length: 8 }, (_, i) => ({ + id: i + 1, + title: `Item ${i + 1}`, + type: (["up", "fade", "scale", "blur", "left", "right"] as const)[i % 6], + })), + ); + const [loading, setLoading] = useState(false); + + const loadMore = useCallback(() => { + if (loading) return; + setLoading(true); + setTimeout(() => { + setItems((prev) => { + const start = prev.length; + const next = Array.from({ length: 5 }, (_, i) => ({ + id: start + i + 1, + title: `Item ${start + i + 1}`, + type: (["up", "fade", "scale", "blur", "left", "right"] as const)[ + (start + i) % 6 + ], + })); + return [...prev, ...next]; + }); + setLoading(false); + }, 600); + }, [loading]); + + const handleScroll = useCallback( + (e: React.UIEvent) => { + const el = e.currentTarget; + if (el.scrollTop + el.clientHeight >= el.scrollHeight - 40) { + loadMore(); + } + }, + [loadMore], + ); + + return ( +
+
+ + {items.length} items loaded + + + Scroll to bottom to load more + +
+ +
+ {items.map((item) => ( + +
+ {item.title} + + {item.type} + +
+
+ ))} + {loading && ( +
+ Loading more... +
+ )} +
+
+
+ ); +}; diff --git a/src/docs/components/ModalDemo.tsx b/src/docs/components/ModalDemo.tsx new file mode 100644 index 0000000..560d8dd --- /dev/null +++ b/src/docs/components/ModalDemo.tsx @@ -0,0 +1,46 @@ +import React, { useState } from "react"; +import { Modal } from "@/components/Modal"; +import { Button } from "@/components/Button"; + +interface ModalDemoProps { + title?: string; + size?: "sm" | "md" | "lg" | "xl"; + showCloseButton?: boolean; +} + +export const ModalDemo: React.FC = ({ + title = "Sample Modal", + size = "md", + showCloseButton = true, +}) => { + const [isOpen, setIsOpen] = useState(false); + + return ( +
+ + + setIsOpen(false)} + title={title} + size={size} + showCloseButton={showCloseButton} + > +
+

+ This is an interactive demonstration of the Modal component. It + supports smooth transitions, backdrop blur, and theme-aware styling. +

+
+ + +
+
+
+
+ ); +}; diff --git a/src/docs/components/PageHeader.tsx b/src/docs/components/PageHeader.tsx new file mode 100644 index 0000000..72dabe2 --- /dev/null +++ b/src/docs/components/PageHeader.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { useTheme } from "@/theme"; + +interface PageHeaderProps { + title: string; + description: string; +} + +export const PageHeader: React.FC = ({ + title, + description, +}) => { + const { theme } = useTheme(); + + return ( +
+
+
+ UI +
+ + Awesome UI + +
+

{title}

+

+ {description} +

+
+ ); +}; diff --git a/src/docs/components/PropsTable.tsx b/src/docs/components/PropsTable.tsx new file mode 100644 index 0000000..2d78f00 --- /dev/null +++ b/src/docs/components/PropsTable.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { useTheme } from "@/theme"; + +interface PropItem { + name: string; + type: string; + defaultValue?: string; + description: string; + required?: boolean; +} + +interface PropsTableProps { + props: PropItem[]; +} + +export const PropsTable: React.FC = ({ props }) => { + const { theme } = useTheme(); + + return ( +
+ + + + + + + + + + + {props.map((prop) => ( + + + + + + + ))} + +
+ Prop + + Type + + Default + + Description +
+ {prop.name} + {prop.required && ( + * + )} + + + {prop.type} + + + {prop.defaultValue || "-"} + + {prop.description} +
+
+ ); +}; diff --git a/src/docs/pages/Accordion.mdx b/src/docs/pages/Accordion.mdx new file mode 100644 index 0000000..dc1fd43 --- /dev/null +++ b/src/docs/pages/Accordion.mdx @@ -0,0 +1,66 @@ +import { Accordion } from '@/components/Accordion'; +import { Typography } from '@/components/Typography'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Basic Usage + +Click the header to expand or collapse the content. + + + A collection of theme-aware React components. +`}> +
+ What is this library?} + subtitle="FAQ" + > +
+ A collection of accessible, theme-aware React components built with Tailwind CSS. +
+
+
+
+ +## Borderless + +Use `variant="borderless"` for a cleaner inline look. + + + Content... +`}> +
+ +
Adjust theme and layout preferences.
+
+ +
Manage data and notification settings.
+
+
+
+ +## Default Open & Disabled + +... +...`}> +
+ + This section starts expanded. + + + Cannot be toggled. + +
+
+ +## Props + diff --git a/src/docs/pages/Avatar.mdx b/src/docs/pages/Avatar.mdx new file mode 100644 index 0000000..d2d567d --- /dev/null +++ b/src/docs/pages/Avatar.mdx @@ -0,0 +1,19 @@ +import { Avatar } from '@/components/Avatar'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PageHeader } from '../components/PageHeader'; + + + +## Basic Usage + +Pass a `src` for an image, `displayName` for initials, or nothing for the default icon. + + + +`}> +
+ + + +
+
diff --git a/src/docs/pages/Badge.mdx b/src/docs/pages/Badge.mdx new file mode 100644 index 0000000..9ffe242 --- /dev/null +++ b/src/docs/pages/Badge.mdx @@ -0,0 +1,53 @@ +import { Badge } from '@/components/Badge'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Variants + +Color-coded badges for different statuses. + +New +Online +Pending +Critical`}> +
+ New + Online + Pending + Critical +
+
+ +## Sizes + +Small +Medium +Large`}> +
+ Small + Medium + Large +
+
+ +## Outline & Loading + +Draft +Loading`}> +
+ Draft + Archived + Loading +
+
+ +## Props + diff --git a/src/docs/pages/Button.mdx b/src/docs/pages/Button.mdx new file mode 100644 index 0000000..fd0a2e7 --- /dev/null +++ b/src/docs/pages/Button.mdx @@ -0,0 +1,74 @@ +import { Button } from '@/components/Button'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Variants + +Each variant signals a different level of emphasis. + +Primary + + +`}> +
+ + + + +
+
+ +## Loading & Disabled + +Shows a spinner during async operations. Disabled prevents interaction. + +Saving... +`}> +
+ + + +
+
+ +## Sizes + +Three sizes: `sm` for toolbars, `md` for forms, `lg` for landing pages. + +Small + +`}> +
+ + + +
+
+ +## Semantic Colors + +Convey intent at a glance — success, warning, or danger. + +Save + +`}> +
+ + + +
+
+ +## Props + diff --git a/src/docs/pages/Checkbox.mdx b/src/docs/pages/Checkbox.mdx new file mode 100644 index 0000000..2eb9abc --- /dev/null +++ b/src/docs/pages/Checkbox.mdx @@ -0,0 +1,56 @@ +import { Checkbox } from '@/components/Checkbox'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Basic Usage + +`}> + + + +## With Description + +`}> + + + +## Indeterminate + +Used for "select all" when only some children are checked. + +`}> + + + +## Sizes + + + +`}> +
+ + + +
+
+ +## Props + diff --git a/src/docs/pages/Dropdown.mdx b/src/docs/pages/Dropdown.mdx new file mode 100644 index 0000000..df7accf --- /dev/null +++ b/src/docs/pages/Dropdown.mdx @@ -0,0 +1,140 @@ +import { Dropdown } from '@/components/Dropdown/Dropdown'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; +import { BasicDemo, IconDemo, ScrollDemo, SearchDemo, SizesDemo, StatesDemo } from '../components/DropdownDemo'; + + + +## Basic Usage + +Use `selectedValue` and `onChange` to control the selected option. Options support `label`, `value`, and `disabled`. + +`}> + + + +## With Icons + +Each option can include an `icon` prop. The icon is displayed in both the trigger and the dropdown list. + + }, + { label: "Bank Transfer", value: "bank", icon: }, + { label: "Crypto", value: "crypto", icon: , disabled: true }, + ]} + selectedValue={value} + onChange={setValue} +/>`}> + + + +## Searchable + +Add `searchable` to enable a filter input inside the panel. Options are filtered as you type, with keyboard navigation still fully supported. + + ({ label: c, value: c.toLowerCase() }))} + selectedValue={value} + onChange={setValue} +/>`}> + + + +## Scrollable List + +When options exceed the panel height, a themed scrollbar appears. Keyboard navigation (Arrow keys, Home, End) auto-scrolls the highlighted item into view. + + ({ label: c, value: c.toLowerCase() }))} + selectedValue={value} + onChange={setValue} +/>`}> + + + +## Sizes + +Three size variants: `sm`, `md` (default), and `lg`. + + + +`}> + + + +## States + +Error validation, disabled state, and loading skeleton. + + + +`}> + + + +## Keyboard Navigation + +| Key | Action | +|-----|--------| +| `Arrow Down` | Open dropdown / highlight next option | +| `Arrow Up` | Open dropdown / highlight previous option | +| `Enter` / `Space` | Select highlighted option / open dropdown | +| `Escape` | Close dropdown | +| `Home` | Jump to first enabled option | +| `End` | Jump to last enabled option | +| Type characters | Filter options (when `searchable` is enabled) | + +## Props + + void', description: 'Callback fired when an option is selected.' }, + { name: 'placeholder', type: 'string', defaultValue: '"Select option"', description: 'Placeholder text when no option is selected.' }, + { name: 'searchable', type: 'boolean', defaultValue: 'false', description: 'Enables a search input inside the dropdown panel.' }, + { name: 'searchPlaceholder', type: 'string', defaultValue: '"Search..."', description: 'Placeholder for the search input.' }, + { name: 'label', type: 'ReactNode', description: 'Label rendered above the trigger.' }, + { name: 'required', type: 'boolean', defaultValue: 'false', description: 'Shows a required indicator on the label.' }, + { name: 'size', type: '"sm" | "md" | "lg"', defaultValue: '"md"', description: 'Size variant of the trigger.' }, + { name: 'error', type: 'string', description: 'Error message displayed below the trigger.' }, + { name: 'disabled', type: 'boolean', defaultValue: 'false', description: 'Disables the dropdown.' }, + { name: 'isLoading', type: 'boolean', defaultValue: 'false', description: 'Shows a skeleton loader.' }, + { name: 'isOpen', type: 'boolean', description: 'Controlled open state.' }, + { name: 'defaultOpen', type: 'boolean', defaultValue: 'false', description: 'Initial open state (uncontrolled).' }, + { name: 'offset', type: 'number', defaultValue: '4', description: 'Gap between trigger and panel in pixels.' }, + { name: 'zIndex', type: 'number', defaultValue: '50', description: 'CSS z-index of the dropdown panel.' }, + { name: 'className', type: 'string', description: 'Custom class for the trigger button.' }, +]} /> + +### DropdownOption + + diff --git a/src/docs/pages/Input.mdx b/src/docs/pages/Input.mdx new file mode 100644 index 0000000..05672ac --- /dev/null +++ b/src/docs/pages/Input.mdx @@ -0,0 +1,69 @@ +import { Input } from '@/components/Input'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Basic Usage + +A labeled input with placeholder and helper text. + +`}> +
+ +
+
+ +## Icons & Loading + +Add context with leading/trailing icons, or show a spinner during validation. + +🔍
} placeholder="Search..." /> +👁️} type="password" /> +`}> +
+ 🔍} placeholder="Search..." /> + 👁️} type="password" placeholder="••••••••" /> + +
+ + +## Validation + +Error messages replace helper text and recolor the border. + +`}> +
+ +
+
+ +## Sizes + + + +`}> +
+ + + +
+
+ +## Props + diff --git a/src/docs/pages/Modal.mdx b/src/docs/pages/Modal.mdx new file mode 100644 index 0000000..06488a2 --- /dev/null +++ b/src/docs/pages/Modal.mdx @@ -0,0 +1,51 @@ +import { ModalDemo } from '../components/ModalDemo'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Basic Usage + +Control visibility with `isOpen` and `onClose`. + + setIsOpen(true)}>Open + setIsOpen(false)} title="Confirm"> + Are you sure? +`}> + + + +## Sizes + +Four widths: `sm`, `md`, `lg`, and `xl`. + + +`}> +
+ + + +
+
+ +## Without Close Button + +Set `showCloseButton={false}` to require an explicit action. + +`}> + + + +## Props + void', required: true, description: 'Called on close.' }, + { name: 'title', type: 'string', description: 'Header text.' }, + { name: 'size', type: '"sm" | "md" | "lg" | "xl"', defaultValue: '"md"', description: 'Dialog width.' }, + { name: 'showCloseButton', type: 'boolean', defaultValue: 'true', description: 'Shows the X button.' }, + { name: 'preventBackdropClose', type: 'boolean', defaultValue: 'false', description: 'Disables backdrop click close.' }, + { name: 'preventEscapeClose', type: 'boolean', defaultValue: 'false', description: 'Disables Escape key close.' }, +]} /> diff --git a/src/docs/pages/Popover.mdx b/src/docs/pages/Popover.mdx new file mode 100644 index 0000000..90014db --- /dev/null +++ b/src/docs/pages/Popover.mdx @@ -0,0 +1,213 @@ +import { Popover } from '@/components/Popover'; +import { Button } from '@/components/Button'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Usage + +Use popovers for contextual actions, mini-menus, or secondary info. Keep content concise. +Avoid for critical workflows—use Modals instead. Always ensure a clear trigger. + +Account Settings}> + +`}> +
+ +

Account Settings

+

View and update your profile.

+ +
+ } + > + + + +
+ +--- + +## Anatomy + +The popover is composed of two parts that live in separate parts of the DOM: a **trigger** (your `children`) and a **floating panel** (your `content`, portalled to ``). An optional arrow bridges the visual gap between them. + +--- + +## Placements & Arrows + +You can control alignment and whether an arrow points directly back to the trigger. + +Additional detailed info goes here.}> + +`}> +
+ Additional detailed info goes here.
} + > + + + +
+ +--- + +## Controlled state + +Use `isOpen` + `onOpenChange` when you need external control — for instance, to close the +popover programmatically after a form submission. + + + +

This popover is fully controlled.

+ + + } + > + +
+ +`}> + {() => { + const [open, setOpen] = useState(false); + return ( +
+ +

This popover is fully controlled.

+ +
+ } + > + + + + + ); + }} +
+ +--- + +## Disabled state + +Set `disabled` to prevent the popover from opening. The trigger is also visually muted +and non-interactive. + +You won't see this.} +> + +`}> + You won't see this.} + > + + + + +--- + +## Nested popovers + +Popovers can be nested — each manages its own open state independently. Click outside the +outermost panel to close all of them. Use `zIndex` to stack panels correctly. + + +

Parent popover

+ +

Child popover

+

Nested content here.

+ + } + > + +
+ + } +> + +`}> + +

Parent popover

+ +

Child popover

+

Nested content here.

+ + } + > + +
+ + } + > + +
+
+ +--- + +## Props + +.' }, + { name: 'content', type: 'ReactNode', description: 'The content rendered inside the floating panel.' }, + { name: 'placement', type: 'Placement', defaultValue: '"bottom"', description: 'Preferred placement relative to the trigger. Floating UI will flip/shift automatically if there is not enough space.' }, + { name: 'isOpen', type: 'boolean', description: 'Controlled open state. Requires onOpenChange to be provided.' }, + { name: 'defaultOpen', type: 'boolean', defaultValue: 'false', description: 'Initial open state for uncontrolled usage.' }, + { name: 'onOpenChange', type: '(open: boolean) => void', description: 'Callback fired when the open state changes.' }, + { name: 'offset', type: 'number', defaultValue: '8', description: 'Gap in px between trigger and panel. Arrow height is added automatically when showArrow is true.' }, + { name: 'showArrow', type: 'boolean', defaultValue: 'false', description: 'Renders a directional arrow pointing from the panel toward the trigger.' }, + { name: 'disabled', type: 'boolean', defaultValue: 'false', description: 'Prevents the popover from opening and mutes the trigger.' }, + { name: 'className', type: 'string', description: 'Extra class names forwarded to the inner panel wrapper.' }, + { name: 'zIndex', type: 'number', defaultValue: '1000', description: 'CSS z-index of the floating panel. Increase when nesting inside other layered elements.' }, + { name: 'id', type: 'string', description: 'Optional id forwarded to the trigger. Defaults to a stable React-generated id.' }, +]} /> \ No newline at end of file diff --git a/src/docs/pages/RadioGroup.mdx b/src/docs/pages/RadioGroup.mdx new file mode 100644 index 0000000..eb4dc08 --- /dev/null +++ b/src/docs/pages/RadioGroup.mdx @@ -0,0 +1,62 @@ +import { RadioGroup } from '@/components/RadioGroup'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Basic Usage + +`}> + + + +## Card Variant + +A more prominent, button-like selection style. + +`}> + + + +## Horizontal + +`}> + + + +## Props + diff --git a/src/docs/pages/ScrollArea.mdx b/src/docs/pages/ScrollArea.mdx new file mode 100644 index 0000000..36b14c8 --- /dev/null +++ b/src/docs/pages/ScrollArea.mdx @@ -0,0 +1,159 @@ +import { ScrollArea, ScrollElement as ScrollAreaItem } from '@/components/ScrollArea'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; +import { InfiniteScrollDemo } from '../components/InfiniteScrollDemo'; + + + +## Infinite Scroll + +Use the `onScroll` callback to detect when the user reaches the bottom and load more items. + + { + const el = e.currentTarget; + if (el.scrollTop + el.clientHeight >= el.scrollHeight - 40) { + loadMore(); // fetch and append items + } + }; + + return ( + + {items.map(item => ( + +
{item.title}
+
+ ))} +
+ ); +}`}> + +
+ +## Vertical Scroll + +Items animate as they enter the viewport. Scroll down through the list. + + +
Item 1
+
Item 2
+
Item 3
+`}> + +
+ +
+ Scroll down to see each animation +
+
+ +
Reveal Up
+
+ +
Reveal Down
+
+ +
Fade In
+
+ +
Scale Pop
+
+ +
Blur Entrance
+
+ +
From Left
+
+ +
From Right
+
+ +
Another Up
+
+ +
Another Scale
+
+ +
Another Blur
+
+ +
End of list
+
+
+
+
+ +## Horizontal Scroll + +Set a wide inner container to enable horizontal scrolling. + + +
+
Card 1
+
Card 2
+
+`}> + +
+ +
+
Card 1
+
Scroll right
+
+
+ +
+
Card 2
+
Horizontal reveal
+
+
+ +
+
Card 3
+
Fade in
+
+
+ +
+
Card 4
+
Scale pop
+
+
+ +
+
Card 5
+
From right
+
+
+
+
+
+ +## Animation Types + +| Type | Effect | +| :--- | :--- | +| `up` / `down` | Slide with fade | +| `left` / `right` | Horizontal slide | +| `fade` | Opacity only | +| `scale` | Zoom in | +| `blur` | Blur to clear | + +## Props + +### ScrollArea + + +### ScrollElement + diff --git a/src/docs/pages/Skeleton.mdx b/src/docs/pages/Skeleton.mdx new file mode 100644 index 0000000..fa8bd93 --- /dev/null +++ b/src/docs/pages/Skeleton.mdx @@ -0,0 +1,62 @@ +import { Skeleton } from '@/components/Skeleton'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Basic Usage + +Combine shapes to mock a content card during loading. + + + +`}> +
+
+ +
+ + +
+
+ +
+ + + +
+
+
+ +## Animations + +Wave sweeps across the surface. Pulse fades in and out. None is static. + + + +`}> +
+
+ Wave + +
+
+ Pulse + +
+
+ None + +
+
+
+ +## Props + diff --git a/src/docs/pages/Switch.mdx b/src/docs/pages/Switch.mdx new file mode 100644 index 0000000..a3db004 --- /dev/null +++ b/src/docs/pages/Switch.mdx @@ -0,0 +1,44 @@ +import { Switch } from '@/components/Switch'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Usage + +Use for immediate actions, like system preferences or binary settings. Avoid for settings requiring a "Save" action—use Checkboxes instead. +Ensure labels are clear and phrased positively (e.g. "Enable Notifications"). + +`}> +
+ +
+
+ +## Sizes & States + +You can provide physical sizes, disabled states, and contextual errors or loading skeletons. + + + +`}> +
+ + + +
+
+ +## Props + void', description: 'Callback fired on toggle.' }, + { name: 'disabled', type: 'boolean', defaultValue: 'false', description: 'Prevents interaction.' }, + { name: 'size', type: '"small" | "medium" | "large"', defaultValue: '"medium"', description: 'Physical size of the switch.' }, + { name: 'label', type: 'ReactNode', description: 'Main label text.' }, + { name: 'description', type: 'ReactNode', description: 'Contextual sub-label text.' }, + { name: 'helperText', type: 'ReactNode', description: 'Text below the switch.' }, + { name: 'error', type: 'ReactNode', description: 'Error state message.' }, + { name: 'isLoading', type: 'boolean', defaultValue: 'false', description: 'Displays skeleton loader.' }, +]} /> diff --git a/src/docs/pages/Theming.mdx b/src/docs/pages/Theming.mdx new file mode 100644 index 0000000..b8056b8 --- /dev/null +++ b/src/docs/pages/Theming.mdx @@ -0,0 +1,219 @@ +import { Button } from '@/components/Button'; +import { Badge } from '@/components/Badge'; +import { Input } from '@/components/Input'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; + +The library ships with a robust, token-based theming system. Every component reads its colors from a centralized theme, so one change updates everything. + +## Quick Start + +Wrap your app in `ThemeProvider` to enable theming across all components. + +```tsx +import { ThemeProvider } from 'ui-library'; + +function App() { + return ( + + + + ); +} +``` + +## Architecture + +The theme system is built on three layers: + +| Layer | Purpose | Example | +| :--- | :--- | :--- | +| **Palette** | Raw color scales (50–900) | `primary.500`, `error.600` | +| **Tokens** | Semantic roles that reference palette values | `background`, `accent`, `border` | +| **CSS Variables** | Applied to `:root` for zero-JS styling | `--color-background`, `--color-accent` | + +This means: +- **No runtime style calculations** — components read CSS variables directly. +- **Zero FOUC** — variables are set before first paint. +- **Full customization** — override any layer to match your brand. + +## Color Modes + +Toggle between light and dark modes with a single hook call. + + + Current: {theme.colorMode} + + ); +}`}> +
+

+ Use the toggle in the top-right header to switch modes. +

+
+
+ +## Dynamic Overrides + +Override the theme at runtime using `overrideTheme`. This is how the color picker on the home page works. + +```tsx +import { useTheme } from 'ui-library'; + +function BrandSwitcher() { + const { overrideTheme, resetTheme } = useTheme(); + + const applyBrand = () => { + overrideTheme({ + palette: { + primary: { 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8' } + }, + tokens: { + accent: '#2563eb', + accentHover: '#1d4ed8', + } + }); + }; + + return ( + <> + + + + ); +} +``` + +Partial overrides are deep-merged — you only need to specify the values you want to change. + +## Registering Custom Themes + +Create a fully custom theme and register it so it can be activated by name. + +```tsx +import { registerTheme, type ThemeConfig } from 'ui-library'; + +const corporateTheme: ThemeConfig = { + name: 'corporate', + colorMode: 'light', + palette: { + primary: { 50: '#eff6ff', 100: '#dbeafe', /* ...full scale */ }, + success: { /* ... */ }, + warning: { /* ... */ }, + error: { /* ... */ }, + info: { /* ... */ }, + }, + neutral: { /* 50–900 gray scale */ }, + tokens: { + background: '#ffffff', + surface: '#f8fafc', + foreground: '#0f172a', + foregroundMuted: '#64748b', + accent: '#2563eb', + // ...all semantic tokens + }, + shape: { + radiusSm: '0.25rem', + radiusMd: '0.5rem', + radiusLg: '0.75rem', + radiusFull: '9999px', + }, + typography: { + fontFamilySans: '"Inter", sans-serif', + fontFamilyMono: '"Fira Code", monospace', + }, +}; + +// Register before rendering +registerTheme(corporateTheme); + +// Then activate it +const { setTheme } = useTheme(); +setTheme('corporate'); +``` + +## Semantic Tokens + +Tokens give colors a **role** instead of a raw value. This makes themes portable — switch the palette and every component adapts. + + + +## useTheme Hook + +The `useTheme` hook provides full access to the theme system. + +```tsx +const { + theme, // Current ThemeConfig object + setTheme, // Switch to a registered theme by name + toggleColorMode, // Toggle light/dark + overrideTheme, // Apply partial overrides + resetTheme, // Clear all overrides +} = useTheme(); +``` + +## useTokens Hook + +A convenience hook that returns just the semantic tokens — useful in components that only need color values. + +```tsx +import { useTokens } from 'ui-library'; + +function Card({ children }) { + const tokens = useTokens(); + + return ( +
+ {children} +
+ ); +} +``` + +## CSS Variables Reference + +All theme values are applied as CSS custom properties on `:root`. You can use them directly in your own CSS or Tailwind classes. + +```css +/* Use in your own stylesheets */ +.my-card { + background: var(--color-surface); + border: 1px solid var(--color-border); + color: var(--color-foreground); + border-radius: var(--radius-md); +} +``` + +| Variable | Source | +| :--- | :--- | +| `--color-background` | `tokens.background` | +| `--color-surface` | `tokens.surface` | +| `--color-foreground` | `tokens.foreground` | +| `--color-border` | `tokens.border` | +| `--color-accent` | `tokens.accent` | +| `--color-ring` | `tokens.ring` | +| `--color-primary-500` | `palette.primary.500` | +| `--radius-sm` / `md` / `lg` | `shape.radius*` | +| `--font-sans` / `--font-mono` | `typography.fontFamily*` | diff --git a/src/docs/pages/ThemingPage.tsx b/src/docs/pages/ThemingPage.tsx new file mode 100644 index 0000000..4617191 --- /dev/null +++ b/src/docs/pages/ThemingPage.tsx @@ -0,0 +1,873 @@ +import React, { useState } from "react"; +import { useTheme } from "@/theme"; +import type { SemanticTokens } from "@/theme"; +import { Button } from "@/components/Button"; +import { Badge } from "@/components/Badge"; +import { Input } from "@/components/Input"; +import { Checkbox } from "@/components/Checkbox"; +import { Dropdown } from "@/components/Dropdown/Dropdown"; +import Switch from "@/components/Switch/Switch"; +import { PageHeader } from "../components/PageHeader"; + +const BRAND_PRESETS = [ + { name: "Pink", color: "#ec4899" }, + { name: "Blue", color: "#3b82f6" }, + { name: "Violet", color: "#8b5cf6" }, + { name: "Emerald", color: "#10b981" }, + { name: "Orange", color: "#f97316" }, + { name: "Red", color: "#ef4444" }, + { name: "Cyan", color: "#06b6d4" }, + { name: "Rose", color: "#f43f5e" }, + { name: "Indigo", color: "#6366f1" }, + { name: "Amber", color: "#f59e0b" }, + { name: "Teal", color: "#14b8a6" }, + { name: "Lime", color: "#84cc16" }, +]; + +function hexToHSL(hex: string): [number, number, number] { + const r = parseInt(hex.slice(1, 3), 16) / 255; + const g = parseInt(hex.slice(3, 5), 16) / 255; + const b = parseInt(hex.slice(5, 7), 16) / 255; + const max = Math.max(r, g, b), + min = Math.min(r, g, b); + const l = (max + min) / 2; + if (max === min) return [0, 0, l]; + const d = max - min; + const s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + let h = 0; + if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6; + else if (max === g) h = ((b - r) / d + 2) / 6; + else h = ((r - g) / d + 4) / 6; + return [h * 360, s * 100, l * 100]; +} + +function hslToHex(h: number, s: number, l: number): string { + s /= 100; + l /= 100; + const a = s * Math.min(l, 1 - l); + const f = (n: number) => { + const k = (n + h / 30) % 12; + const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); + return Math.round(255 * Math.max(0, Math.min(1, color))) + .toString(16) + .padStart(2, "0"); + }; + return `#${f(0)}${f(8)}${f(4)}`; +} + +function generateScale(hex: string) { + const [h, s] = hexToHSL(hex); + return { + 50: hslToHex(h, Math.min(100, s * 1.1), 97), + 100: hslToHex(h, Math.min(100, s * 1.1), 93), + 200: hslToHex(h, Math.min(100, s), 85), + 300: hslToHex(h, Math.min(100, s * 0.95), 73), + 400: hslToHex(h, Math.min(100, s * 0.9), 60), + 500: hex, + 600: hslToHex(h, Math.min(100, s * 0.95), 42), + 700: hslToHex(h, Math.min(100, s * 0.9), 34), + 800: hslToHex(h, Math.min(100, s * 0.85), 26), + 900: hslToHex(h, Math.min(100, s * 0.8), 18), + }; +} + +const CodeBlock: React.FC<{ children: string }> = ({ children }) => { + const { theme } = useTheme(); + const [copied, setCopied] = useState(false); + + return ( +
+
+        {children}
+      
+ +
+ ); +}; + +const Section: React.FC<{ + title: string; + description?: string; + children: React.ReactNode; +}> = ({ title, description, children }) => { + const { theme } = useTheme(); + return ( +
+

{title}

+ {description && ( +

+ {description} +

+ )} + {children} +
+ ); +}; + +// ─── Dark Theme Presets ──────────────────────────────────────────────────── + +interface ThemePreset { + name: string; + description: string; + preview: [string, string, string]; // 3 colors for the preview swatch + neutral: Record; + tokens: Partial; +} + +const DARK_PRESETS: ThemePreset[] = [ + { + name: "Midnight", + description: "Pure black with neutral grays", + preview: ["#080808", "#181818", "#f2f2f2"], + neutral: { + 50: "#080808", + 100: "#101010", + 200: "#181818", + 300: "#202020", + 400: "#383838", + 500: "#585858", + 600: "#787878", + 700: "#a0a0a0", + 800: "#c8c8c8", + 900: "#f2f2f2", + }, + tokens: { + background: "#080808", + surface: "#101010", + surfaceHover: "#181818", + elevated: "#181818", + foreground: "#f2f2f2", + foregroundMuted: "#a0a0a0", + foregroundSubtle: "#787878", + foregroundInverse: "#0b0f14", + border: "#202020", + borderSubtle: "#181818", + borderStrong: "#383838", + input: "#181818", + inputHover: "#202020", + inputFocus: "#101010", + ringOffset: "#101010", + }, + }, + { + name: "Ocean", + description: "Deep blue-tinted dark theme", + preview: ["#0a1628", "#111d33", "#e2e8f0"], + neutral: { + 50: "#0a1628", + 100: "#0f1d2e", + 200: "#111d33", + 300: "#1a2a42", + 400: "#2a3f5f", + 500: "#4a6182", + 600: "#6b82a0", + 700: "#8fa3bb", + 800: "#c4d1de", + 900: "#e2e8f0", + }, + tokens: { + background: "#0a1628", + surface: "#0f1d2e", + surfaceHover: "#111d33", + elevated: "#111d33", + foreground: "#e2e8f0", + foregroundMuted: "#8fa3bb", + foregroundSubtle: "#6b82a0", + foregroundInverse: "#0a1628", + border: "#1a2a42", + borderSubtle: "#111d33", + borderStrong: "#2a3f5f", + input: "#111d33", + inputHover: "#1a2a42", + inputFocus: "#0f1d2e", + ringOffset: "#0f1d2e", + }, + }, + { + name: "Slate", + description: "Cool gray with blue undertones", + preview: ["#0f172a", "#1e293b", "#f1f5f9"], + neutral: { + 50: "#0f172a", + 100: "#1e293b", + 200: "#1e293b", + 300: "#334155", + 400: "#475569", + 500: "#64748b", + 600: "#94a3b8", + 700: "#cbd5e1", + 800: "#e2e8f0", + 900: "#f1f5f9", + }, + tokens: { + background: "#0f172a", + surface: "#1e293b", + surfaceHover: "#273549", + elevated: "#1e293b", + foreground: "#f1f5f9", + foregroundMuted: "#94a3b8", + foregroundSubtle: "#64748b", + foregroundInverse: "#0f172a", + border: "#334155", + borderSubtle: "#1e293b", + borderStrong: "#475569", + input: "#1e293b", + inputHover: "#334155", + inputFocus: "#1e293b", + ringOffset: "#1e293b", + }, + }, + { + name: "Violet Night", + description: "Dark purple-tinted surfaces", + preview: ["#0e0a1a", "#1a1425", "#ede9fe"], + neutral: { + 50: "#0e0a1a", + 100: "#141020", + 200: "#1a1425", + 300: "#251e35", + 400: "#3b3255", + 500: "#5b5278", + 600: "#7c739a", + 700: "#a09abc", + 800: "#cbc5de", + 900: "#ede9fe", + }, + tokens: { + background: "#0e0a1a", + surface: "#141020", + surfaceHover: "#1a1425", + elevated: "#1a1425", + foreground: "#ede9fe", + foregroundMuted: "#a09abc", + foregroundSubtle: "#7c739a", + foregroundInverse: "#0e0a1a", + border: "#251e35", + borderSubtle: "#1a1425", + borderStrong: "#3b3255", + input: "#1a1425", + inputHover: "#251e35", + inputFocus: "#141020", + ringOffset: "#141020", + }, + }, + { + name: "Emerald Dark", + description: "Forest green undertones", + preview: ["#071210", "#0d1f1b", "#ecfdf5"], + neutral: { + 50: "#071210", + 100: "#0a1a17", + 200: "#0d1f1b", + 300: "#162e28", + 400: "#264a40", + 500: "#3d6b5e", + 600: "#5a8c7c", + 700: "#86b5a2", + 800: "#b8d8ca", + 900: "#ecfdf5", + }, + tokens: { + background: "#071210", + surface: "#0a1a17", + surfaceHover: "#0d1f1b", + elevated: "#0d1f1b", + foreground: "#ecfdf5", + foregroundMuted: "#86b5a2", + foregroundSubtle: "#5a8c7c", + foregroundInverse: "#071210", + border: "#162e28", + borderSubtle: "#0d1f1b", + borderStrong: "#264a40", + input: "#0d1f1b", + inputHover: "#162e28", + inputFocus: "#0a1a17", + ringOffset: "#0a1a17", + }, + }, + { + name: "Warm Dark", + description: "Cozy dark with warm brown tones", + preview: ["#120f0b", "#1c1814", "#faf5ef"], + neutral: { + 50: "#120f0b", + 100: "#181510", + 200: "#1c1814", + 300: "#2a2520", + 400: "#3e3830", + 500: "#5c5448", + 600: "#7a7165", + 700: "#a09688", + 800: "#c8bfb2", + 900: "#faf5ef", + }, + tokens: { + background: "#120f0b", + surface: "#181510", + surfaceHover: "#1c1814", + elevated: "#1c1814", + foreground: "#faf5ef", + foregroundMuted: "#a09688", + foregroundSubtle: "#7a7165", + foregroundInverse: "#120f0b", + border: "#2a2520", + borderSubtle: "#1c1814", + borderStrong: "#3e3830", + input: "#1c1814", + inputHover: "#2a2520", + inputFocus: "#181510", + ringOffset: "#181510", + }, + }, +]; + +const LIGHT_PRESETS: ThemePreset[] = [ + { + name: "Default", + description: "Clean white with neutral grays", + preview: ["#fbfaf9", "#ffffff", "#111827"], + neutral: { + 50: "#fbfaf9", + 100: "#f3f4f6", + 200: "#e5e7eb", + 300: "#d1d5db", + 400: "#9ca3af", + 500: "#6b7280", + 600: "#4b5563", + 700: "#374151", + 800: "#1f2937", + 900: "#111827", + }, + tokens: { + background: "#fbfaf9", + surface: "#ffffff", + surfaceHover: "#f3f4f6", + elevated: "#ffffff", + foreground: "#111827", + foregroundMuted: "#6b7280", + foregroundSubtle: "#9ca3af", + foregroundInverse: "#ffffff", + border: "#e5e7eb", + borderSubtle: "#f3f4f6", + borderStrong: "#9ca3af", + input: "#ffffff", + inputHover: "#fbfaf9", + inputFocus: "#ffffff", + ringOffset: "#ffffff", + }, + }, + { + name: "Snow Blue", + description: "Soft blue-tinted light theme", + preview: ["#f0f4f8", "#ffffff", "#1a2b4a"], + neutral: { + 50: "#f0f4f8", + 100: "#e8edf2", + 200: "#d5dce6", + 300: "#b8c4d4", + 400: "#8a9ab5", + 500: "#5d7096", + 600: "#445a7a", + 700: "#344763", + 800: "#25344c", + 900: "#1a2b4a", + }, + tokens: { + background: "#f0f4f8", + surface: "#ffffff", + surfaceHover: "#e8edf2", + elevated: "#ffffff", + foreground: "#1a2b4a", + foregroundMuted: "#5d7096", + foregroundSubtle: "#8a9ab5", + foregroundInverse: "#ffffff", + border: "#d5dce6", + borderSubtle: "#e8edf2", + borderStrong: "#8a9ab5", + input: "#ffffff", + inputHover: "#f0f4f8", + inputFocus: "#ffffff", + ringOffset: "#ffffff", + }, + }, + { + name: "Warm Sand", + description: "Warm cream backgrounds", + preview: ["#faf6f0", "#ffffff", "#2d1f10"], + neutral: { + 50: "#faf6f0", + 100: "#f3ede4", + 200: "#e5ddd0", + 300: "#d1c5b4", + 400: "#a89680", + 500: "#7a6b58", + 600: "#5c4f3e", + 700: "#443a2e", + 800: "#322b20", + 900: "#2d1f10", + }, + tokens: { + background: "#faf6f0", + surface: "#ffffff", + surfaceHover: "#f3ede4", + elevated: "#ffffff", + foreground: "#2d1f10", + foregroundMuted: "#7a6b58", + foregroundSubtle: "#a89680", + foregroundInverse: "#ffffff", + border: "#e5ddd0", + borderSubtle: "#f3ede4", + borderStrong: "#a89680", + input: "#ffffff", + inputHover: "#faf6f0", + inputFocus: "#ffffff", + ringOffset: "#ffffff", + }, + }, +]; + +// ─── Theme Preset Card ──────────────────────────────────────────────────── + +const PresetCard: React.FC<{ + preset: ThemePreset; + active: boolean; + onSelect: () => void; +}> = ({ preset, active, onSelect }) => { + const { theme } = useTheme(); + return ( + + ); +}; + +// ─── Main Page ──────────────────────────────────────────────────────────── + +export const ThemingPage: React.FC = () => { + const { theme, overrideTheme, resetTheme, toggleColorMode } = useTheme(); + const [activePreset, setActivePreset] = useState(theme.palette.primary[500]); + const [activeVariation, setActiveVariation] = useState(null); + const [ddValue, setDdValue] = useState(""); + const [switchOn, setSwitchOn] = useState(false); + const [checked, setChecked] = useState(true); + + const variations = theme.colorMode === "dark" ? DARK_PRESETS : LIGHT_PRESETS; + + const applyColor = (color: string) => { + setActivePreset(color); + const scale = generateScale(color); + overrideTheme({ + palette: { primary: scale }, + tokens: { accent: scale[600], accentHover: scale[700], ring: scale[500] }, + }); + }; + + const applyVariation = (preset: ThemePreset) => { + setActiveVariation(preset.name); + overrideTheme({ + neutral: preset.neutral as unknown as typeof theme.neutral, + tokens: preset.tokens, + }); + }; + + const handleReset = () => { + setActivePreset("#ec4899"); + setActiveVariation(null); + resetTheme(); + }; + + return ( +
+ + + {/* Installation */} +
+ {`npm install awesome-ui-library`} +
+ + {/* Setup */} +
+ {`import { ThemeProvider } from 'awesome-ui-library'; + +function App() { + return ( + + + + ); +}`} +
+ + {/* How it works */} +
+
+ {[ + { + step: "1", + layer: "Palette", + desc: "Raw color scales from 50 to 900.", + code: "palette.primary[500]", + }, + { + step: "2", + layer: "Tokens", + desc: "Semantic roles like accent and border.", + code: "tokens.accent", + }, + { + step: "3", + layer: "CSS Variables", + desc: "Injected into :root. No JS at render time.", + code: "--color-accent", + }, + ].map((item) => ( +
+
+ + {item.step} + + {item.layer} +
+

+ {item.desc} +

+ + {item.code} + +
+ ))} +
+
+ + {/* Color Modes */} +
+
+
+
+ Active:{" "} + + {theme.colorMode} + +
+

+ Try it — the entire site will adapt. +

+
+ +
+ {`const { toggleColorMode } = useTheme(); +toggleColorMode(); // light ↔ dark`} +
+ + {/* Live Brand Switcher */} +
+
+ {/* Brand pills */} +
+ {BRAND_PRESETS.map((p) => ( + + ))} + +
+ + {/* Theme Variations */} +
+
+ {theme.colorMode === "dark" ? "Dark" : "Light"} Theme Variations +
+
+ {variations.map((preset) => ( + applyVariation(preset)} + /> + ))} +
+
+ + {/* Live preview */} +
+ Live Preview +
+
+
+ + + + +
+ + + +
+ Active + Online + Pending +
+ +
+ + +
+ + +
+
+ {`const { overrideTheme } = useTheme(); + +overrideTheme({ + palette: { + primary: { 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8' } + }, + tokens: { + accent: '#2563eb', + accentHover: '#1d4ed8', + } +});`} +
+ + {/* Hooks API */} +
+
+
+
useTheme()
+

+ Full control — read the theme, switch modes, apply overrides, or + reset. +

+
+
+
useTokens()
+

+ Just the semantic tokens. Use when a component only needs color + values. +

+
+
+ {`// Full access +const { theme, setTheme, toggleColorMode, overrideTheme, resetTheme } = useTheme(); + +// Just colors +const tokens = useTokens(); +// tokens.background, tokens.accent, tokens.border, ...`} +
+ + {/* CSS Variables */} +
+ {`.card { + background: var(--color-surface); + border: 1px solid var(--color-border); + color: var(--color-foreground); + border-radius: var(--radius-md); +} + +/* Available variables: + --color-background, --color-surface, --color-foreground + --color-border, --color-accent, --color-ring + --color-primary-50 through --color-primary-900 + --radius-sm, --radius-md, --radius-lg + --font-sans, --font-mono +*/`} +
+
+ ); +}; diff --git a/src/docs/pages/Typography.mdx b/src/docs/pages/Typography.mdx new file mode 100644 index 0000000..1259184 --- /dev/null +++ b/src/docs/pages/Typography.mdx @@ -0,0 +1,66 @@ +import { Typography } from '@/components/Typography'; +import { ComponentPreview } from '../components/ComponentPreview'; +import { PropsTable } from '../components/PropsTable'; +import { PageHeader } from '../components/PageHeader'; + + + +## Display + +Large-scale text for hero sections and landing pages. + +Display 1 +Display 2`}> +
+ Display 1 + Display 2 +
+
+ +## Headings + +Semantic headings H1 through H3. + +Heading 1 +Heading 2 +Heading 3`}> +
+ Heading 1 + Heading 2 + Heading 3 +
+
+ +## Body & Caption + +Paragraph and meta text styles. + +Body 1 +Body 2 +Caption`}> +
+ Body 1 — standard paragraph text. + Body 2 — smaller secondary text. + Caption — metadata and labels. +
+
+ +## Weight Overrides + +Override the default weight on any variant. + +Bold H4 +Thin H4`}> +
+ Bold H4 + Thin H4 +
+
+ +## Props + diff --git a/src/mdx.d.ts b/src/mdx.d.ts new file mode 100644 index 0000000..2e0f342 --- /dev/null +++ b/src/mdx.d.ts @@ -0,0 +1,5 @@ +declare module "*.mdx" { + import type { ComponentType } from "react"; + const Component: ComponentType; + export default Component; +} diff --git a/src/theme/ThemeProvider.tsx b/src/theme/ThemeProvider.tsx index ae8640e..8da5e99 100644 --- a/src/theme/ThemeProvider.tsx +++ b/src/theme/ThemeProvider.tsx @@ -70,7 +70,18 @@ export function ThemeProvider({ return defaultThemeName; }); - const [override, setOverride] = useState(null); + const [override, setOverride] = useState(() => { + if (storageKey) { + const stored = localStorage.getItem(`${storageKey}-override`); + try { + return stored ? JSON.parse(stored) : null; + } catch (e) { + console.error("Failed to parse theme override from storage", e); + return null; + } + } + return null; + }); const baseTheme = THEME_REGISTRY[activeThemeName] ?? defaultTheme; const theme = useMemo( @@ -78,7 +89,6 @@ export function ThemeProvider({ [baseTheme, override], ); - // Apply CSS variables whenever the theme changes useEffect(() => { applyTheme(theme); }, [theme]); @@ -91,7 +101,10 @@ export function ThemeProvider({ } setActiveThemeName(name); setOverride(null); - if (storageKey) localStorage.setItem(storageKey, name); + if (storageKey) { + localStorage.setItem(storageKey, name); + localStorage.removeItem(`${storageKey}-override`); + } }, [storageKey], ); @@ -103,15 +116,38 @@ export function ThemeProvider({ ); if (nextTheme) { setActiveThemeName(nextTheme.name); + // Keep palette (primary color) but clear surface overrides + setOverride((prev) => { + if (!prev?.palette) return null; + const kept = { palette: prev.palette }; + if (storageKey) { + localStorage.setItem(`${storageKey}-override`, JSON.stringify(kept)); + } + return kept; + }); if (storageKey) localStorage.setItem(storageKey, nextTheme.name); } }, [theme.colorMode, storageKey]); - const overrideTheme = useCallback((o: ThemeOverride) => { - setOverride((prev) => (prev ? { ...prev, ...o } : o)); - }, []); + const overrideTheme = useCallback( + (o: ThemeOverride) => { + setOverride((prev) => { + const next = prev ? { ...prev, ...o } : o; + if (storageKey) { + localStorage.setItem(`${storageKey}-override`, JSON.stringify(next)); + } + return next; + }); + }, + [storageKey], + ); - const resetTheme = useCallback(() => setOverride(null), []); + const resetTheme = useCallback(() => { + setOverride(null); + if (storageKey) { + localStorage.removeItem(`${storageKey}-override`); + } + }, [storageKey]); const value = useMemo( () => ({ theme, setTheme, toggleColorMode, overrideTheme, resetTheme }), diff --git a/tailwind.config.js b/tailwind.config.js index 5bd1efd..6877844 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,6 +1,6 @@ /** @type {import('tailwindcss').Config} */ export default { - content: ['./src/**/*.{js,ts,jsx,tsx}'], + content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], darkMode: 'class', theme: { extend: { @@ -12,7 +12,18 @@ export default { border: 'var(--color-border)', accent: 'var(--color-accent)', ring: 'var(--color-ring)', - // ...etc + primary: { + 50: 'var(--color-primary-50)', + 100: 'var(--color-primary-100)', + 200: 'var(--color-primary-200)', + 300: 'var(--color-primary-300)', + 400: 'var(--color-primary-400)', + 500: 'var(--color-primary-500)', + 600: 'var(--color-primary-600)', + 700: 'var(--color-primary-700)', + 800: 'var(--color-primary-800)', + 900: 'var(--color-primary-900)', + }, }, borderRadius: { sm: 'var(--radius-sm)', @@ -25,5 +36,7 @@ export default { }, }, }, - plugins: [], + plugins: [ + require('@tailwindcss/typography'), + ], }; \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index e1dda2c..428baae 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,6 +3,7 @@ import react from "@vitejs/plugin-react"; import { resolve, dirname } from "path"; import { fileURLToPath } from "url"; import dts from "vite-plugin-dts"; +import mdx from "@mdx-js/rollup"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -10,10 +11,11 @@ const __dirname = dirname(__filename); // https://vite.dev/config/ export default defineConfig({ plugins: [ + mdx(), react(), dts({ include: ["src"], - exclude: ["**/*.test.tsx", "**/*.test.ts"], + exclude: ["**/*.test.tsx", "**/*.test.ts", "**/*.mdx"], rollupTypes: false, }), ],