diff --git a/.prettierrc b/.prettierrc
index 0967ef4..3e915a0 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1 +1,8 @@
-{}
+{
+ "overrides": [
+ {
+ "files": "*.svx",
+ "options": { "parser": "mdx" }
+ }
+ ]
+}
diff --git a/mdsvex.config.js b/mdsvex.config.js
new file mode 100644
index 0000000..6785763
--- /dev/null
+++ b/mdsvex.config.js
@@ -0,0 +1,11 @@
+import rehypeSlug from "rehype-slug";
+import rehypeAutolinkHeadings from "rehype-autolink-headings";
+
+const config = {
+ extensions: [".svelte.md", ".md", ".svx"],
+
+ remarkPlugins: [],
+ rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: "wrap" }]],
+};
+
+export default config;
diff --git a/package-lock.json b/package-lock.json
index 9b43a25..8210b35 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,9 +11,11 @@
"devDependencies": {
"@babel/cli": "^7.16.0",
"@babel/preset-env": "^7.16.5",
+ "@rgossiaux/svelte-heroicons": "^0.1.2",
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next",
"@tailwindcss/forms": "^0.4.0",
+ "@tailwindcss/typography": "^0.5.0",
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/svelte": "^3.0.3",
"@types/jest": "^27.0.3",
@@ -26,10 +28,13 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-svelte3": "^3.2.1",
"jest": "^27.4.5",
+ "mdsvex": "^0.9.8",
"postcss": "^8.3.9",
"postcss-load-config": "^3.1.0",
"prettier": "^2.4.1",
"prettier-plugin-svelte": "^2.4.0",
+ "rehype-autolink-headings": "^6.1.1",
+ "rehype-slug": "^5.0.1",
"svelte": "^3.44.0",
"svelte-check": "^2.4.1",
"svelte-inline-compile": "^0.0.1",
@@ -2346,6 +2351,15 @@
"url": "https://opencollective.com/popperjs"
}
},
+ "node_modules/@rgossiaux/svelte-heroicons": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@rgossiaux/svelte-heroicons/-/svelte-heroicons-0.1.2.tgz",
+ "integrity": "sha512-c5Ep1QDvBo9HD/P0AxbXItDbn6x77fldCjjL0aBjNseUntV4fkdHkBde1IaLr8i0kmrhTSovjkIen8W83jUPzg==",
+ "dev": true,
+ "peerDependencies": {
+ "svelte": "^3.44.0"
+ }
+ },
"node_modules/@rollup/pluginutils": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz",
@@ -3226,6 +3240,21 @@
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1"
}
},
+ "node_modules/@tailwindcss/typography": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.0.tgz",
+ "integrity": "sha512-1p/3C6C+JJziS/ghtG8ACYalbA2SyLJY27Pm33cVTlAoY6VQ7zfm2H64cPxUMBkVIlWXTtWHhZcZJPobMRmQAA==",
+ "dev": true,
+ "dependencies": {
+ "lodash.castarray": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.merge": "^4.6.2",
+ "lodash.uniq": "^4.5.0"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || insiders"
+ }
+ },
"node_modules/@testing-library/dom": {
"version": "7.31.2",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
@@ -3418,6 +3447,15 @@
"@types/node": "*"
}
},
+ "node_modules/@types/hast": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
+ "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@@ -3506,6 +3544,12 @@
"@types/jest": "*"
}
},
+ "node_modules/@types/unist": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
+ "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
+ "dev": true
+ },
"node_modules/@types/yargs": {
"version": "16.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
@@ -4110,6 +4154,16 @@
"@babel/core": "^7.0.0"
}
},
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "dev": true,
+ "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",
@@ -5733,6 +5787,12 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "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
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -5959,6 +6019,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/github-slugger": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz",
+ "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==",
+ "dev": true
+ },
"node_modules/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
@@ -6065,6 +6131,56 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/hast-util-has-property": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-2.0.0.tgz",
+ "integrity": "sha512-4Qf++8o5v14us4Muv3HRj+Er6wTNGA/N9uCaZMty4JWvyFKLdhULrv4KE1b65AthsSO9TXSZnjuxS8ecIyhb0w==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-heading-rank": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-2.1.0.tgz",
+ "integrity": "sha512-w+Rw20Q/iWp2Bcnr6uTrYU6/ftZLbHKhvc8nM26VIWpDqDMlku2iXUVTeOlsdoih/UKQhY7PHQ+vZ0Aqq8bxtQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-element": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.2.tgz",
+ "integrity": "sha512-thjnlGAnwP8ef/GSO1Q8BfVk2gundnc2peGQqEg2kUt/IqesiGg/5mSwN2fE7nLzy61pg88NG6xV+UrGOrx9EA==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "@types/unist": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz",
+ "integrity": "sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/html-encoding-sniffer": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
@@ -6266,6 +6382,29 @@
"node": ">=8"
}
},
+ "node_modules/is-buffer": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
+ "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/is-core-module": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
@@ -6326,6 +6465,18 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-plain-obj": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.0.0.tgz",
+ "integrity": "sha512-NXRbBtUdBioI73y/HmOhogw/U5msYPC9DAtGkJXeFcFWSFZw0mCUsPxk/snTuJHzNKA8kLBK4rH97RMB1BfCXw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -7263,12 +7414,24 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "node_modules/lodash.castarray": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
+ "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
+ "dev": true
+ },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
"dev": true
},
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
+ "dev": true
+ },
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -7377,6 +7540,21 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true
},
+ "node_modules/mdsvex": {
+ "version": "0.9.8",
+ "resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.9.8.tgz",
+ "integrity": "sha512-5QvThjRKoKkGH00qdHxLZ5ROd80RgGiJvM2B9opeFreaiGFTLoKKFUgEBCslLrwM24cVGJLmIM3rR83OFDf3tQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.3",
+ "prism-svelte": "^0.4.7",
+ "prismjs": "^1.17.1",
+ "vfile-message": "^2.0.4"
+ },
+ "peerDependencies": {
+ "svelte": "3.x"
+ }
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -8416,6 +8594,21 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/prism-svelte": {
+ "version": "0.4.7",
+ "resolved": "https://registry.npmjs.org/prism-svelte/-/prism-svelte-0.4.7.tgz",
+ "integrity": "sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==",
+ "dev": true
+ },
+ "node_modules/prismjs": {
+ "version": "1.26.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz",
+ "integrity": "sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@@ -8614,6 +8807,44 @@
"jsesc": "bin/jsesc"
}
},
+ "node_modules/rehype-autolink-headings": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz",
+ "integrity": "sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "extend": "^3.0.0",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-heading-rank": "^2.0.0",
+ "hast-util-is-element": "^2.0.0",
+ "unified": "^10.0.0",
+ "unist-util-visit": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-slug": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-5.0.1.tgz",
+ "integrity": "sha512-X5v3wV/meuOX9NFcGhJvUpEjIvQl2gDvjg3z40RVprYFt7q3th4qMmYLULiu3gXvbNX1ppx+oaa6JyY1W67pTA==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "github-slugger": "^1.1.1",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-heading-rank": "^2.0.0",
+ "hast-util-to-string": "^2.0.0",
+ "unified": "^10.0.0",
+ "unist-util-visit": "^4.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",
@@ -9492,6 +9723,16 @@
"node": ">=8"
}
},
+ "node_modules/trough": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz",
+ "integrity": "sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/ts-jest": {
"version": "27.1.2",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.2.tgz",
@@ -9658,6 +9899,77 @@
"node": ">=4"
}
},
+ "node_modules/unified": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz",
+ "integrity": "sha512-v4ky1+6BN9X3pQrOdkFIPWAaeDsHPE1svRDxq7YpTc2plkIqFMwukfqM+l0ewpP9EfwARlt9pPFAeWYhHm8X9w==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "bail": "^2.0.0",
+ "extend": "^3.0.0",
+ "is-buffer": "^2.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.1.tgz",
+ "integrity": "sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
+ "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.0.tgz",
+ "integrity": "sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^5.0.0",
+ "unist-util-visit-parents": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz",
+ "integrity": "sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@@ -9702,6 +10014,63 @@
"node": ">=10.12.0"
}
},
+ "node_modules/vfile": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.0.tgz",
+ "integrity": "sha512-Tj44nY/48OQvarrE4FAjUfrv7GZOYzPbl5OD65HxVKwLJKMPU7zmfV8cCgCnzKWnSfYG2f3pxu+ALqs7j22xQQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "is-buffer": "^2.0.0",
+ "unist-util-stringify-position": "^3.0.0",
+ "vfile-message": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
+ "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-stringify-position": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile/node_modules/unist-util-stringify-position": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz",
+ "integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile/node_modules/vfile-message": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.0.tgz",
+ "integrity": "sha512-4QJbBk+DkPEhBXq3f260xSaWtjE4gPKOfulzfMFF8ZNwaPZieWsg3iVlcmF04+eebzpcpeXOOFMfrYzJHVYg+g==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-stringify-position": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/vite": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.7.2.tgz",
@@ -11884,6 +12253,13 @@
"dev": true,
"peer": true
},
+ "@rgossiaux/svelte-heroicons": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@rgossiaux/svelte-heroicons/-/svelte-heroicons-0.1.2.tgz",
+ "integrity": "sha512-c5Ep1QDvBo9HD/P0AxbXItDbn6x77fldCjjL0aBjNseUntV4fkdHkBde1IaLr8i0kmrhTSovjkIen8W83jUPzg==",
+ "dev": true,
+ "requires": {}
+ },
"@rollup/pluginutils": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz",
@@ -12424,6 +12800,18 @@
"mini-svg-data-uri": "^1.2.3"
}
},
+ "@tailwindcss/typography": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.0.tgz",
+ "integrity": "sha512-1p/3C6C+JJziS/ghtG8ACYalbA2SyLJY27Pm33cVTlAoY6VQ7zfm2H64cPxUMBkVIlWXTtWHhZcZJPobMRmQAA==",
+ "dev": true,
+ "requires": {
+ "lodash.castarray": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.merge": "^4.6.2",
+ "lodash.uniq": "^4.5.0"
+ }
+ },
"@testing-library/dom": {
"version": "7.31.2",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
@@ -12588,6 +12976,15 @@
"@types/node": "*"
}
},
+ "@types/hast": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
+ "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "*"
+ }
+ },
"@types/istanbul-lib-coverage": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@@ -12676,6 +13073,12 @@
"@types/jest": "*"
}
},
+ "@types/unist": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
+ "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
+ "dev": true
+ },
"@types/yargs": {
"version": "16.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
@@ -13094,6 +13497,12 @@
"babel-preset-current-node-syntax": "^1.0.0"
}
},
+ "bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "dev": true
+ },
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -14283,6 +14692,12 @@
}
}
},
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -14459,6 +14874,12 @@
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"dev": true
},
+ "github-slugger": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz",
+ "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==",
+ "dev": true
+ },
"glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
@@ -14532,6 +14953,40 @@
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
+ "hast-util-has-property": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-2.0.0.tgz",
+ "integrity": "sha512-4Qf++8o5v14us4Muv3HRj+Er6wTNGA/N9uCaZMty4JWvyFKLdhULrv4KE1b65AthsSO9TXSZnjuxS8ecIyhb0w==",
+ "dev": true
+ },
+ "hast-util-heading-rank": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-2.1.0.tgz",
+ "integrity": "sha512-w+Rw20Q/iWp2Bcnr6uTrYU6/ftZLbHKhvc8nM26VIWpDqDMlku2iXUVTeOlsdoih/UKQhY7PHQ+vZ0Aqq8bxtQ==",
+ "dev": true,
+ "requires": {
+ "@types/hast": "^2.0.0"
+ }
+ },
+ "hast-util-is-element": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.2.tgz",
+ "integrity": "sha512-thjnlGAnwP8ef/GSO1Q8BfVk2gundnc2peGQqEg2kUt/IqesiGg/5mSwN2fE7nLzy61pg88NG6xV+UrGOrx9EA==",
+ "dev": true,
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "@types/unist": "^2.0.0"
+ }
+ },
+ "hast-util-to-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz",
+ "integrity": "sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==",
+ "dev": true,
+ "requires": {
+ "@types/hast": "^2.0.0"
+ }
+ },
"html-encoding-sniffer": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
@@ -14684,6 +15139,12 @@
"binary-extensions": "^2.0.0"
}
},
+ "is-buffer": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
+ "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
+ "dev": true
+ },
"is-core-module": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
@@ -14726,6 +15187,12 @@
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
+ "is-plain-obj": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.0.0.tgz",
+ "integrity": "sha512-NXRbBtUdBioI73y/HmOhogw/U5msYPC9DAtGkJXeFcFWSFZw0mCUsPxk/snTuJHzNKA8kLBK4rH97RMB1BfCXw==",
+ "dev": true
+ },
"is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -15460,12 +15927,24 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "lodash.castarray": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
+ "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
+ "dev": true
+ },
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
"dev": true
},
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
+ "dev": true
+ },
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -15561,6 +16040,18 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true
},
+ "mdsvex": {
+ "version": "0.9.8",
+ "resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.9.8.tgz",
+ "integrity": "sha512-5QvThjRKoKkGH00qdHxLZ5ROd80RgGiJvM2B9opeFreaiGFTLoKKFUgEBCslLrwM24cVGJLmIM3rR83OFDf3tQ==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.3",
+ "prism-svelte": "^0.4.7",
+ "prismjs": "^1.17.1",
+ "vfile-message": "^2.0.4"
+ }
+ },
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -16262,6 +16753,18 @@
}
}
},
+ "prism-svelte": {
+ "version": "0.4.7",
+ "resolved": "https://registry.npmjs.org/prism-svelte/-/prism-svelte-0.4.7.tgz",
+ "integrity": "sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==",
+ "dev": true
+ },
+ "prismjs": {
+ "version": "1.26.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz",
+ "integrity": "sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ==",
+ "dev": true
+ },
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@@ -16408,6 +16911,36 @@
}
}
},
+ "rehype-autolink-headings": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz",
+ "integrity": "sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA==",
+ "dev": true,
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "extend": "^3.0.0",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-heading-rank": "^2.0.0",
+ "hast-util-is-element": "^2.0.0",
+ "unified": "^10.0.0",
+ "unist-util-visit": "^4.0.0"
+ }
+ },
+ "rehype-slug": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-5.0.1.tgz",
+ "integrity": "sha512-X5v3wV/meuOX9NFcGhJvUpEjIvQl2gDvjg3z40RVprYFt7q3th4qMmYLULiu3gXvbNX1ppx+oaa6JyY1W67pTA==",
+ "dev": true,
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "github-slugger": "^1.1.1",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-heading-rank": "^2.0.0",
+ "hast-util-to-string": "^2.0.0",
+ "unified": "^10.0.0",
+ "unist-util-visit": "^4.0.0"
+ }
+ },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -17037,6 +17570,12 @@
"punycode": "^2.1.1"
}
},
+ "trough": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz",
+ "integrity": "sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==",
+ "dev": true
+ },
"ts-jest": {
"version": "27.1.2",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.2.tgz",
@@ -17140,6 +17679,57 @@
"integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==",
"dev": true
},
+ "unified": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz",
+ "integrity": "sha512-v4ky1+6BN9X3pQrOdkFIPWAaeDsHPE1svRDxq7YpTc2plkIqFMwukfqM+l0ewpP9EfwARlt9pPFAeWYhHm8X9w==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "bail": "^2.0.0",
+ "extend": "^3.0.0",
+ "is-buffer": "^2.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^5.0.0"
+ }
+ },
+ "unist-util-is": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.1.tgz",
+ "integrity": "sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==",
+ "dev": true
+ },
+ "unist-util-stringify-position": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
+ "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.2"
+ }
+ },
+ "unist-util-visit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.0.tgz",
+ "integrity": "sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^5.0.0",
+ "unist-util-visit-parents": "^5.0.0"
+ }
+ },
+ "unist-util-visit-parents": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz",
+ "integrity": "sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^5.0.0"
+ }
+ },
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@@ -17178,6 +17768,49 @@
"source-map": "^0.7.3"
}
},
+ "vfile": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.0.tgz",
+ "integrity": "sha512-Tj44nY/48OQvarrE4FAjUfrv7GZOYzPbl5OD65HxVKwLJKMPU7zmfV8cCgCnzKWnSfYG2f3pxu+ALqs7j22xQQ==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "is-buffer": "^2.0.0",
+ "unist-util-stringify-position": "^3.0.0",
+ "vfile-message": "^3.0.0"
+ },
+ "dependencies": {
+ "unist-util-stringify-position": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz",
+ "integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0"
+ }
+ },
+ "vfile-message": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.0.tgz",
+ "integrity": "sha512-4QJbBk+DkPEhBXq3f260xSaWtjE4gPKOfulzfMFF8ZNwaPZieWsg3iVlcmF04+eebzpcpeXOOFMfrYzJHVYg+g==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-stringify-position": "^3.0.0"
+ }
+ }
+ }
+ },
+ "vfile-message": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
+ "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
+ "dev": true,
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-stringify-position": "^2.0.0"
+ }
+ },
"vite": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.7.2.tgz",
diff --git a/package.json b/package.json
index 22b3d43..52b0e2f 100644
--- a/package.json
+++ b/package.json
@@ -33,9 +33,11 @@
"devDependencies": {
"@babel/cli": "^7.16.0",
"@babel/preset-env": "^7.16.5",
+ "@rgossiaux/svelte-heroicons": "^0.1.2",
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next",
"@tailwindcss/forms": "^0.4.0",
+ "@tailwindcss/typography": "^0.5.0",
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/svelte": "^3.0.3",
"@types/jest": "^27.0.3",
@@ -48,10 +50,13 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-svelte3": "^3.2.1",
"jest": "^27.4.5",
+ "mdsvex": "^0.9.8",
"postcss": "^8.3.9",
"postcss-load-config": "^3.1.0",
"prettier": "^2.4.1",
"prettier-plugin-svelte": "^2.4.0",
+ "rehype-autolink-headings": "^6.1.1",
+ "rehype-slug": "^5.0.1",
"svelte": "^3.44.0",
"svelte-check": "^2.4.1",
"svelte-inline-compile": "^0.0.1",
diff --git a/postcss.config.cjs b/postcss.config.cjs
index 11da48c..28b7b6d 100644
--- a/postcss.config.cjs
+++ b/postcss.config.cjs
@@ -1,6 +1,7 @@
const tailwindcss = require("tailwindcss");
const autoprefixer = require("autoprefixer");
const cssnano = require("cssnano");
+const nesting = require("tailwindcss/nesting");
const mode = process.env.NODE_ENV;
const dev = mode === "development";
@@ -8,13 +9,14 @@ const dev = mode === "development";
const config = {
plugins: [
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
+ nesting(),
tailwindcss(),
//But others, like autoprefixer, need to run after,
autoprefixer(),
!dev &&
- cssnano({
- preset: "default",
- }),
+ cssnano({
+ preset: "default",
+ }),
],
};
diff --git a/src/app.html b/src/app.html
index 68b819a..e5b0eed 100644
--- a/src/app.html
+++ b/src/app.html
@@ -3,7 +3,7 @@
-
+
%svelte.head%
diff --git a/src/lib/utils/run-with-cleanup.ts b/src/lib/utils/run-with-cleanup.ts
new file mode 100644
index 0000000..fa1681f
--- /dev/null
+++ b/src/lib/utils/run-with-cleanup.ts
@@ -0,0 +1,24 @@
+import { onDestroy } from "svelte";
+
+export function createRunWithCleanup() {
+ let cleanup: { [key: string]: () => any } = {};
+
+ onDestroy(() => {
+ for (const id in cleanup) {
+ cleanup[id]();
+ delete cleanup[id];
+ }
+ })
+
+ return (fn: () => any, id: string) => {
+ if (cleanup[id]) {
+ cleanup[id]();
+ delete cleanup[id];
+ }
+
+ const result = fn();
+ if (typeof result === "function") {
+ cleanup[id] = result;
+ }
+ }
+}
diff --git a/src/routes/__error.svelte b/src/routes/__error.svelte
new file mode 100644
index 0000000..af90318
--- /dev/null
+++ b/src/routes/__error.svelte
@@ -0,0 +1,6 @@
+
+
Error
+
This page could not be loaded.
+
diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte
index 881d2ba..11f8ae0 100644
--- a/src/routes/__layout.svelte
+++ b/src/routes/__layout.svelte
@@ -2,4 +2,23 @@
import "../app.css";
+
diff --git a/src/routes/docs/_Preview.svelte b/src/routes/docs/_Preview.svelte
new file mode 100644
index 0000000..900f276
--- /dev/null
+++ b/src/routes/docs/_Preview.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
+{code}
diff --git a/src/routes/docs/_TableOfContents.svelte b/src/routes/docs/_TableOfContents.svelte
new file mode 100644
index 0000000..e0e50c2
--- /dev/null
+++ b/src/routes/docs/_TableOfContents.svelte
@@ -0,0 +1,129 @@
+
+
+
+
+
diff --git a/src/routes/docs/_TocItems.svelte b/src/routes/docs/_TocItems.svelte
new file mode 100644
index 0000000..b9f8f56
--- /dev/null
+++ b/src/routes/docs/_TocItems.svelte
@@ -0,0 +1,38 @@
+
+
+
+
+
+ {#each items as item (item.url)}
+
+ {item.title}
+ {#if item.items}
+
+ {/if}
+
+ {/each}
+
+
+
diff --git a/src/routes/docs/__layout.svelte b/src/routes/docs/__layout.svelte
new file mode 100644
index 0000000..91ed059
--- /dev/null
+++ b/src/routes/docs/__layout.svelte
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+ {#each pages as p (p.url)}
+ {p.text}
+ {/each}
+
+ {#each components as component (component.url)}
+ {component.text}
+ {/each}
+
+
+
+
+
+
+
+ {#key $page}
+
+ {/key}
+
+
+
+
+
diff --git a/src/routes/docs/dialog.svx b/src/routes/docs/dialog.svx
new file mode 100644
index 0000000..a5a80d6
--- /dev/null
+++ b/src/routes/docs/dialog.svx
@@ -0,0 +1,393 @@
+# Dialog
+
+
+
+
+
+## Basic example
+
+Dialogs are built using the `Dialog`, `DialogOverlay`, `DialogTitle`, and `DialogDescription` components.
+
+When the dialog's `open` prop is `true`, the contents of the dialog will render. Focus will be moved inside the dialog and trapped there as the user cycles through the focusable elements. Scrolling is locked, the rest of your application UI is hidden from screen readers, and clicking outside the dialog or pressing the Escape key will fire the `close` event and close the dialog.
+
+```svelte
+
+
+ (isOpen = false)}>
+
+
+ Deactivate account
+
+ This will permanently deactivate your account
+
+
+
+ Are you sure you want to deactivate your account? All of your data will be
+ permanently removed. This action cannot be undone.
+
+
+ (isOpen = false)}>Deactivate
+ (isOpen = false)}>Cancel
+
+```
+
+## Showing and hiding your dialog
+
+`Dialog` has no automatic management of its open/closed state. To show and hide your dialog, pass in a boolean value to the `open` prop. When `open` is true the dialog will render, and when it's false the dialog will unmount.
+
+The `close` event is fired when an open dialog is dismissed, which happens when the user clicks outside of the contents of your dialog or presses the Escape key. You can use this callback to set `open` back to false and close your dialog.
+
+```svelte
+
+
+
+ (isOpen = false)}>
+
+
+ Deactivate account
+
+ This will permanently deactivate your account
+
+
+
+ Are you sure you want to deactivate your account? All of your data will be
+ permanently removed. This action cannot be undone.
+
+
+
+ (isOpen = false)}>Deactivate
+ (isOpen = false)}>Cancel
+
+```
+
+## Managing focus within your dialog
+
+For accessibility reasons, your dialog should contain at least one focusable element. By default, the `Dialog` component will focus the first focusable element (by DOM order) when it is rendered, and pressing the Tab key will cycle through all additional focusable elements within the contents.
+
+Focus is trapped within the dialog as long as it is rendered, so tabbing to the end will start cycling back through the beginning again. All other application elements outside of the dialog will be marked as inert and thus not focusable.
+
+If you'd like something other than the first focusable element to receive initial focus when your dialog is initially rendered, you can use the `initialFocus` prop:
+
+```svelte
+
+
+ (isOpen = false)}
+ initialFocus={completeButton}
+>
+
+
+ Complete your order
+
+ Your order is all ready!
+
+ (isOpen = false)}> Cancel
+
+ Complete order
+
+
+```
+
+## Styling
+
+[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
+
+### Styling the overlay
+
+Typically modal dialogs will be rendered on top of a transparent dark background. You can style the `DialogOverlay` component to achieve this look.
+
+The overlay component accepts normal styling props like `style` and `class`, so you can style it using any technique you like. Be sure to place it before the rest of your dialog's contents in the DOM so that it doesn't obscure the dialog's interactive elements.
+
+```svelte
+
+
+ (isOpen = false)}>
+
+
+
+```
+
+### Styling the dialog
+
+There's nothing special about the contents of your dialog--you can use whatever HTML and CSS you please. Typical dialogs will have a max width and be centered in the screen, but fullscreen treatments on smaller screens are also common.
+
+## Using the Title and Description components
+
+For accessibility reasons, you should use the `DialogTitle` and `DialogDescription` components when rendering content that labels and describes your dialog contents. They will be automatically linked to the root `Dialog` component via the `aria-labelledby` and `aria-describedby` attributes, and their contents will be announced to users using screenreaders.
+
+```svelte
+
+
+ (isOpen = false)}>
+
+
+ Deactivate account
+
+ This will permanently deactivate your account
+
+
+
+
+```
+
+## Rendering to a Portal
+
+If you've ever implemented a dialog before in another framework, you may have come across the concept of portals. Portals let you insert components from one place in the component tree (for instance, deep within your application UI), but have them actually render to another place in the DOM entirely.
+
+Since dialogs and their overlays take up the full page, you typically want to render them as a sibling to the root node of the page. That way, you can rely on natural DOM ordering to ensure that their content is rendered on top of your existing application UI. This also makes it easy to apply scroll locking to the rest of your application, as well as ensure that your Dialog's contents and overlay are unobstructed to receive focus and click events.
+
+Because of these accessibility concerns, Svelte Headless UI's `Dialog` actually uses a portal under the hood. This way we can provide features like unobstructed event handling and making the rest of your application inert. That means that when using this `Dialog`, there's no need to use a portal yourself--it's already taken care of. You can put the `` anywhere in your component tree and it will render to the portal.
+
+## Transitions
+
+To animate the opening and closing of your dialog, you can use [this library's Transition component](/docs/transition) or Svelte's built-in transition engine. See that page for a comparison.
+
+### Using the `Transition` component
+
+To use the `Transition` component, all you need to do is wrap the `Dialog` in a ``, and the dialog will transition automatically based on the value of the `show` prop on the ``. You can remove the `open` prop on the ``, as the dialog will read the `show` value from the `` automatically.
+
+```svelte
+
+
+
+
+ (isOpen = false)}>
+
+ Deactivate account
+
+
+
+
+```
+
+To animate the dialog's overlay and contents separately, use `Transition` and `TransitionChild`:
+
+```svelte
+
+
+
+
+
+ (isOpen = false)}>
+
+
+
+
+
+
+
+ Deactivate account
+
+
+
+
+
+```
+
+### Using Svelte transitions
+
+If you wish to animate your dialogs using another technique (like Svelte's built-in transitions), you can use the `static` prop to tell the component to not manage rendering itself, so you can control it manually in another way.
+
+```svelte
+
+
+{#if isOpen}
+
+ (isOpen = false)} static>
+
+ Deactivate account
+
+
+
+
+{/if}
+```
+
+The `open` prop is still used for managing scroll locking and focus trapping, but as long as `static` is present, the actual element will always be rendered regardless of the `open` value, which allows you to control it yourself externally. Without `static`, the exit transitions won't work correctly.
+
+## Accessibility notes
+
+### Focus management
+
+When the dialog's `open` prop is `true`, the contents of the dialog will render and focus will be moved inside the dialog and trapped there. The first focusable element according to DOM order will receive focus, although you can use the `initialFocus` prop to control which element receives initial focus. Pressing Tab on an open dialog cycles through all the focusable elements.
+
+### Mouse interaction
+
+When a `Dialog` is rendered, clicking the `DialogOverlay` will close the `Dialog`.
+
+No mouse interaction to open the `Dialog` is included out-of-the-box, though typically you will wire a ` ` element up with an `on:click` handler that toggles the dialog's `open` prop to `true`.
+
+### Keyboard interaction
+
+| Command | Description |
+| ------------------- | -------------------------------------------------- |
+| `` | Closes any open dialogs |
+| `` | Cycles through an open dialog's contents |
+| `` + `` | Cycles backwards through an open dialog's contents |
+
+### Other
+
+When a Dialog is open, scroll is locked and the rest of your application UI is hidden from screen readers.
+
+All relevant ARIA attributes are automatically managed.
+
+For a full reference on all accessibility features implemented in `Dialog`, see the ARIA spec on Dialogs .
+
+## Component API
+
+### Dialog
+
+The main dialog component.
+
+| Prop | Default | Type | Description |
+| -------------- | ------- | ------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
+| `open` | -- | `boolean` | Whether the `Dialog` is open |
+| `initialFocus` | `null` | `HTMLElement` \| `null` | The element that should receive focus when the `Dialog` is first opened |
+| `as` | `div` | `string` | The element the `Dialog` should render as |
+| `static` | `false` | `boolean` | Whether the element should ignore the internally managed open/closed state |
+| `unmount` | `true` | `boolean` | Whether the element should be unmounted, instead of just hidden, based on the open/closed state |
+
+Note that `static` and `unmount` cannot be used together.
+
+| Slot prop | Type | Description |
+| --------- | --------- | --------------------------------- |
+| `open` | `boolean` | Whether or not the dialog is open |
+
+This component also dispatches a custom event, which is listened to using the Svelte `on:` directive:
+
+| Event name | Type of event `.detail` | Description |
+| ---------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
+| `close` | `null` | Emitted when the `Dialog` is dismissed (via the overlay or Escape key). Typically used to close the dialog by setting `open` to false. |
+
+### DialogOverlay
+
+This can be used to create an overlay for your dialog. Clicking on the overlay will close the dialog.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ------------------------------------------------ |
+| `as` | `div` | `string` | The element the `DialogOverlay` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | --------------------------------- |
+| `open` | `boolean` | Whether or not the dialog is open |
+
+### DialogTitle
+
+This is the title for your dialog. When this is used, it will set the `aria-labelledby` on the dialog.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ---------------------------------------------- |
+| `as` | `h2` | `string` | The element the `DialogTitle` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | --------------------------------- |
+| `open` | `boolean` | Whether or not the dialog is open |
+
+### DialogDescription
+
+This is the description for your dialog. When this is used, it will set the `aria-describedby` on the dialog.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ---------------------------------------------------- |
+| `as` | `p` | `string` | The element the `DialogDescription` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | --------------------------------- |
+| `open` | `boolean` | Whether or not the dialog is open |
diff --git a/src/routes/docs/disclosure.svx b/src/routes/docs/disclosure.svx
new file mode 100644
index 0000000..9a4c4c3
--- /dev/null
+++ b/src/routes/docs/disclosure.svx
@@ -0,0 +1,308 @@
+# Disclosure
+
+
+
+
+
+## Basic example
+
+Disclosures are built using the `Disclosure`, `DisclosureButton`, and `DisclosurePanel` components.
+
+Clicking the `DisclosureButton` will automatically open/close the `DisclosurePanel`, and all components will receive the appropriate `aria-*` attributes like `aria-expanded` and `aria-controls`.
+
+```svelte
+
+
+
+ Is team pricing available?
+
+
+ Yes! You can purchase a license that you can share with your entire team.
+
+
+```
+
+## Styling
+
+[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
+
+### Open panels
+
+`Disclosure` and its related components expose a slot prop containing the `open` state of the panel. You can use this to apply whatever styles you wish.
+
+```svelte
+
+
+
+
+ Is team pricing available?
+
+
+
+
+
+ Yes! You can purchase a license that you can share with your entire team.
+
+
+```
+
+## Showing/hiding the panel
+
+By default, your `DisclosurePanel` will be shown/hidden automatically based on the internal open state tracked within the `Disclosure` component itself.
+
+If you'd rather handle this yourself (perhaps because you need to add an extra wrapper element for one reason or another), you can add a `static` prop to the `DisclosurePanel` component to tell it to always render, and use the `open` slot prop to show or hide the panel yourself.
+
+```svelte
+
+
+
+ Is team pricing available?
+
+ {#if open}
+
+
+
+ Yes! You can purchase a license that you can share with your entire
+ team.
+
+
+ {/if}
+
+```
+
+## Closing disclosures manually
+
+To close a disclosure manually when clicking a child of its panel, render that child as a `DisclosureButton`. You can use the `as` prop to customize which element is being rendered.
+
+```svelte
+
+
+
+ Open mobile menu
+
+ Home
+
+
+
+```
+
+This is especially useful when using disclosures for things like mobile menus that contain links, where you want the disclosure to close when navigating to the next page.
+
+Alternatively, `Disclosure` and `DisclosurePanel` expose a `close()` slot prop which you can use to imperatively close the panel:
+
+```svelte
+
+
+
+ Solutions
+
+ {
+ await fetch("/accept-terms", { method: "POST" });
+ close();
+ }}
+ >
+ Read and accept
+
+
+
+
+```
+
+By default the `DisclosureButton` receives focus after calling `close()`, but you can change this by passing an element into `close(el)`.
+
+## Transitions
+
+To animate the opening and closing of the disclosure panel, you can use [this library's Transition component](/docs/transition) or Svelte's built-in transition engine. See that page for a comparison.
+
+### Using the `Transition` component
+
+To use the `Transition` component, all you need to do is wrap the `DisclosurePanel` in a `` and the panel will transition automatically.
+
+```svelte
+
+
+
+ Is team pricing available?
+
+
+
+
+
+
+
+```
+
+The components in this library communicate with each other, so the Transition will be managed automatically when the Listbox is opened/closed. If you require more control over this behavior, you may use a more explicit version:
+
+```svelte
+
+
+
+ Is team pricing available?
+
+
+
+
+
+
+
+
+```
+
+### Using Svelte transitions
+
+The last example above also provides a blueprint for using Svelte transitions:
+
+```svelte
+
+
+
+ Is team pricing available?
+ {#if open}
+
+
+
+
+
+
+ {/if}
+
+```
+
+Make sure to use the `static` prop, or else the exit transitions won't work correctly.
+
+## Accessibility notes
+
+### Mouse interaction
+
+Clicking a `DisclosureButton` toggles the disclosure's panel open and closed.
+
+### Keyboard interaction
+
+| Command | Description |
+| ---------------------------------------------------------- | ------------- |
+| `` / `` when a `DisclosureButton` is focused | Toggles panel |
+
+### Other
+
+All relevant ARIA attributes are automatically managed.
+
+For a full reference on all accessibility features implemented in `Disclosure`, see the ARIA spec on Disclosure .
+
+## Component API
+
+### Disclosure
+
+The main disclosure component.
+
+| Prop | Default | Type | Description |
+| ------------- | ------- | --------- | -------------------------------------------------- |
+| `as` | `div` | `string` | The element the `Disclosure` should render as |
+| `defaultOpen` | `false` | `boolean` | Whether the `Disclosure` should be open by default |
+
+| Slot prop | Type | Description |
+| --------- | ---------------------------- | ----------------------------------------------------------------------------------- |
+| `open` | `boolean` | Whether the disclosure is open |
+| `close` | `(el?: HTMLElement) => void` | Closes the disclosure and focuses `el`, if passed, or the `DisclosureButton` if not |
+
+### DisclosureButton
+
+This is the trigger button to toggle a disclosure.
+
+You can also use this `DisclosureButton` component inside a `DisclosurePanel`. If you do, it will behave as a close button and have the appropriate `aria-*` attributes.
+
+| Prop | Default | Type | Description |
+| ---- | -------- | -------- | --------------------------------------------------- |
+| `as` | `button` | `string` | The element the `DisclosureButton` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | ------------------------------------- |
+| `open` | `boolean` | Whether or not the disclosure is open |
+
+### DisclosurePanel
+
+This component contains the contents of your disclosure.
+
+| Prop | Default | Type | Description |
+| --------- | ------- | --------- | ----------------------------------------------------------------------------------------------- |
+| `as` | `div` | `string` | The element the `DisclosurePanel` should render as |
+| `static` | `false` | `boolean` | Whether the element should ignore the internally managed open/closed state |
+| `unmount` | `true` | `boolean` | Whether the element should be unmounted, instead of just hidden, based on the open/closed state |
+
+Note that `static` and `unmount` cannot be used together.
+
+| Slot prop | Type | Description |
+| --------- | ---------------------------- | ----------------------------------------------------------------------------------- |
+| `open` | `boolean` | Whether or not the disclosure is open |
+| `close` | `(el?: HTMLElement) => void` | Closes the disclosure and focuses `el`, if passed, or the `DisclosureButton` if not |
diff --git a/src/routes/docs/examples/_PopoverIconOne.svelte b/src/routes/docs/examples/_PopoverIconOne.svelte
new file mode 100644
index 0000000..9cbf899
--- /dev/null
+++ b/src/routes/docs/examples/_PopoverIconOne.svelte
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/src/routes/docs/examples/_PopoverIconThree.svelte b/src/routes/docs/examples/_PopoverIconThree.svelte
new file mode 100644
index 0000000..7b89325
--- /dev/null
+++ b/src/routes/docs/examples/_PopoverIconThree.svelte
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/routes/docs/examples/_PopoverIconTwo.svelte b/src/routes/docs/examples/_PopoverIconTwo.svelte
new file mode 100644
index 0000000..8d69725
--- /dev/null
+++ b/src/routes/docs/examples/_PopoverIconTwo.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/src/routes/docs/examples/__layout.reset.svelte b/src/routes/docs/examples/__layout.reset.svelte
new file mode 100644
index 0000000..6088b65
--- /dev/null
+++ b/src/routes/docs/examples/__layout.reset.svelte
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/routes/docs/examples/dialog.svelte b/src/routes/docs/examples/dialog.svelte
new file mode 100644
index 0000000..77902dd
--- /dev/null
+++ b/src/routes/docs/examples/dialog.svelte
@@ -0,0 +1,90 @@
+
+
+
+
+ Open dialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Payment successful
+
+
+
+ Your payment has been successfully submitted. We’ve sent you an
+ email with all of the details of your order.
+
+
+
+
+
+ Got it, thanks!
+
+
+
+
+
+
+
diff --git a/src/routes/docs/examples/disclosure.svelte b/src/routes/docs/examples/disclosure.svelte
new file mode 100644
index 0000000..c385e22
--- /dev/null
+++ b/src/routes/docs/examples/disclosure.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ What is your refund policy?
+
+
+
+ If you're unhappy with your purchase for any reason, email us within 90
+ days and we'll refund you in full, no questions asked.
+
+
+
+
+ Do you offer technical support?
+
+
+
+ No.
+
+
+
+
diff --git a/src/routes/docs/examples/listbox.svelte b/src/routes/docs/examples/listbox.svelte
new file mode 100644
index 0000000..54bf927
--- /dev/null
+++ b/src/routes/docs/examples/listbox.svelte
@@ -0,0 +1,73 @@
+
+
+
+
(selected = e.detail)}>
+
+
+ {selected.name}
+
+
+
+
+
+
+ {#each people as person, personIdx (personIdx)}
+
+ `cursor-default select-none relative py-2 pl-10 pr-4 ${
+ active ? "text-amber-900 bg-amber-100" : "text-gray-900"
+ }`}
+ value={person}
+ let:selected
+ >
+
+ {person.name}
+
+ {#if selected}
+
+
+
+ {/if}
+
+ {/each}
+
+
+
+
+
diff --git a/src/routes/docs/examples/menu.svelte b/src/routes/docs/examples/menu.svelte
new file mode 100644
index 0000000..f25639a
--- /dev/null
+++ b/src/routes/docs/examples/menu.svelte
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+ Options
+
+
+
+
+
+
+
+
+
+
+
+
+ Edit
+
+
+
+
+
+
+
+
+
+
+ Duplicate
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archive
+
+
+
+
+
+
+
+
+
+
+ Move
+
+
+
+
+
+
+
+
+
+
+
+
+ Delete
+
+
+
+
+
+
+
diff --git a/src/routes/docs/examples/popover.svelte b/src/routes/docs/examples/popover.svelte
new file mode 100644
index 0000000..180ed70
--- /dev/null
+++ b/src/routes/docs/examples/popover.svelte
@@ -0,0 +1,99 @@
+
+
+
+
+
+ Solutions
+
+
+
+
+
+
+
+
+
diff --git a/src/routes/docs/examples/radio-group.svelte b/src/routes/docs/examples/radio-group.svelte
new file mode 100644
index 0000000..0ff81f8
--- /dev/null
+++ b/src/routes/docs/examples/radio-group.svelte
@@ -0,0 +1,98 @@
+
+
+
+
+
(selected = e.detail)}>
+ Server size
+
+ {#each plans as plan (plan.name)}
+
+ `${
+ active
+ ? "ring-2 ring-offset-2 ring-offset-sky-300 ring-white ring-opacity-60"
+ : ""
+ }
+ ${
+ checked ? "bg-sky-900 bg-opacity-75 text-white" : "bg-white"
+ }
+ relative rounded-lg shadow-md px-5 py-4 cursor-pointer flex focus:outline-none`}
+ let:checked
+ >
+
+
+
+
+ {plan.name}
+
+
+
+ {plan.ram}/{plan.cpus}
+ {" "}
+ · {" "}
+ {plan.disk}
+
+
+
+ {#if checked}
+
+ {/if}
+
+
+ {/each}
+
+
+
+
diff --git a/src/routes/docs/examples/switch.svelte b/src/routes/docs/examples/switch.svelte
new file mode 100644
index 0000000..a679d71
--- /dev/null
+++ b/src/routes/docs/examples/switch.svelte
@@ -0,0 +1,21 @@
+
+
+
+ (enabled = e.detail)}
+ class={`${enabled ? "bg-teal-900" : "bg-teal-700"}
+ relative inline-flex flex-shrink-0 h-[38px] w-[74px] border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
+ >
+ Use setting
+
+
+
diff --git a/src/routes/docs/examples/tabs.svelte b/src/routes/docs/examples/tabs.svelte
new file mode 100644
index 0000000..a7752f6
--- /dev/null
+++ b/src/routes/docs/examples/tabs.svelte
@@ -0,0 +1,117 @@
+
+
+
+
+
+ {#each Object.keys(categories) as category (category)}
+
+ classNames(
+ "w-full py-2.5 text-sm leading-5 font-medium text-blue-700 rounded-lg",
+ "focus:outline-none focus:ring-2 ring-offset-2 ring-offset-blue-400 ring-white ring-opacity-60",
+ selected
+ ? "bg-white shadow"
+ : "text-blue-100 hover:bg-white/[0.12] hover:text-white"
+ )}
+ >
+ {category}
+
+ {/each}
+
+
+ {#each Object.values(categories) as posts, idx (idx)}
+
+
+ {#each posts as post (post.id)}
+
+
+ {post.title}
+
+
+
+ {post.date}
+ ·
+ {post.commentCount} comments
+ ·
+ {post.shareCount} shares
+
+
+
+
+ {/each}
+
+
+ {/each}
+
+
+
diff --git a/src/routes/docs/examples/transition.svelte b/src/routes/docs/examples/transition.svelte
new file mode 100644
index 0000000..9ac1357
--- /dev/null
+++ b/src/routes/docs/examples/transition.svelte
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Click to transition
+
+
diff --git a/src/routes/docs/general-concepts.svx b/src/routes/docs/general-concepts.svx
new file mode 100644
index 0000000..447ca5e
--- /dev/null
+++ b/src/routes/docs/general-concepts.svx
@@ -0,0 +1,425 @@
+# General concepts
+
+## Component styling
+
+Out of the box, these components are completely unstyled. This has some big advantages: you're free to make them look however you want, without having to fight against any pre-existing styles that don't fit in with the look and feel of your site. Instead of being constrained by an existing design system like Material Design or Carbon Components, you can fully customize your components' appearance--but letting the library take care of implementing all the keyboard and mouse interactions, accessibility features, and so on.
+
+The flipside of unstyled components, however, is that you need to, well, style them. There are a number of ways of doing this.
+
+### Styling children: slot props
+
+You can style the descendants of components from this library with the help of slot props :
+
+```svelte
+
+
+ (plan = e.detail)}>
+ Plan
+
+ Startup
+
+
+ Business
+
+
+ Enterprise
+
+
+
+
+```
+
+### Styling the components themselves: `class` and `style`
+
+Slot props can be used to style any descendent components, but they cannot be used to style the same component that defines them. To work around this, any component that defines slot props will also optionally accept a **function** for its `class` and/or `style` props. These functions will be **called with the slot props as an argument**:
+
+```svelte
+
+
+ (plan = e.detail)}>
+ Plan
+ (checked ? "checked" : undefined)}
+ >
+ Startup
+
+ (checked ? "checked" : undefined)}
+ >
+ Business
+
+ (checked ? "checked" : undefined)}
+ >
+ Enterprise
+
+
+
+
+```
+
+See the individual component documentation to learn about the slot props for each component.
+
+You may also pass a string for `class` or `style`, or omit them entirely, of course.
+
+What follows below is some guidance on the different ways to use the `class` and `style` props to style your components.
+
+#### Using Svelte's `
+```
+
+This works great for styling HTML elements. However, it (unfortunately) does **not** work for styling _components_. If you try to create the same example without the ``s, it won't work:
+
+```svelte
+
+
+ (plan = e.detail)}>
+ Plan
+ (checked ? "checked" : "")}
+ >
+ Startup
+
+ (checked ? "checked" : "")}
+ >
+ Business
+
+ (checked ? "checked" : "")}
+ >
+ Enterprise
+
+
+
+
+
+```
+
+(Note that we also can no longer use the `class:checked` shorthand on a component)
+
+Why doesn't this work? When Svelte compiles our component, it adds in a special unique class to the CSS rules and to the HTML elements rendered by this component that are affected. But it does _not_ add this special class to any child _components_. The result is that the `.checked` CSS rule will be generated with a second class that the `` components do not have, and it will not match.
+
+Unfortunately there is no way in Svelte right now to pass this special class down to the `` components, and the framework does not have a good solution in general for this. Hopefully one day the Svelte maintainers will add some way of solving this problem. Until then, you can still style your components using one of the approaches below.
+
+#### The `:global()` modifier
+
+The `:global()` modifier opts your rule out of the default component style scoping. CSS rules inside `:global()` are treated as "normal" CSS, so the following will work:
+
+```svelte
+
+
+ (plan = e.detail)}>
+ Plan
+ (checked ? "checked" : "")}
+ >
+ Startup
+
+ (checked ? "checked" : "")}
+ >
+ Business
+
+ (checked ? "checked" : "")}
+ >
+ Enterprise
+
+
+
+
+
+```
+
+However, global CSS has one critical downside: it's global! The `.checked` rule above will now apply to _any_ `.checked` CSS class in your application, even if it's in other components. In a larger application, this can get difficult to maintain.
+
+You can scope this more narrowly if you have a wrapper element:
+
+```svelte
+
+
+
+ (plan = e.detail)}>
+ Plan
+ (checked ? "checked" : "")}
+ >
+ Startup
+
+ (checked ? "checked" : "")}
+ >
+ Business
+
+ (checked ? "checked" : "")}
+ >
+ Enterprise
+
+
+
+
+
+
+```
+
+In this case, this works perfectly.
+
+When using this scoped `* > :global()` approach, keep in mind these principles:
+
+- Your Headless UI components must be contained inside some element **that is rendered by your component**, like the wrapper `` above. You need an element for the Svelte scoped class to be attached to.
+- Your rule will be scoped to your component **and any descendant**. In the example above this is not a problem, since the component has no `
`s. But if your component _does_ have ``s, you will still need to be careful for collisions.
+- This rule won't work properly inside a portal, which means that it won't work properly for a ``. To style the ``, you will need to either 1) use global styles without the `* >` scoping, 2) put a wrapper element inside the `` and style that instead, or 3) use one of the other styling approaches.
+
+#### Inline styles
+
+You can style any component using inline styles using the `style=...` prop:
+
+```svelte
+
+
+
+ (plan = e.detail)}>
+ Plan
+
+ checked ? "background-color: rgb(191 219 254)" : undefined}
+ >
+ Startup
+
+
+ checked ? "background-color: rgb(191 219 254)" : undefined}
+ >
+ Business
+
+
+ checked ? "background-color: rgb(191 219 254)" : undefined}
+ >
+ Enterprise
+
+
+
+```
+
+As mentioned above, this prop accepts a function whose input will be the component's slot props, allowing you to apply conditional styles.
+
+One downside of inline styles is that you cannot use them to style CSS psuedoselectors, like `:focus` and `:hover`. In general this library will provide you with states that make these psuedoselectors unnecessary, however.
+
+#### Using a CSS framework
+
+You can always use the `class` prop as normal alongside a CSS framework like Bootstrap, Bulma, Tailwind, etc. The original version of this component library was written by the same team that maintains Tailwind CSS.
+
+## Event forwarding
+
+This library will forward all events to the underlying elements, so you can add your own event handlers if necessary:
+
+```svelte
+
+
+ (enabled = e.detail)}
+ on:click={() => console.log("Clicked!")}
+>
+ Toggle me!
+
+```
+
+Note that the library already handles all of the keyboard and mouse events you'd need to implement the component functionality. You don't need to add a handler to e.g. open or close a popover when the popover button is clicked.
+
+You can also use event modifiers, but they must be separated by ! instead of the normal |, for technical reasons:
+
+```svelte
+
+
+ (enabled = e.detail)}
+ on:click!once={() => console.log("Clicked!")}
+>
+ Toggle me!
+
+```
+
+## Using Svelte actions
+
+You can use [actions](https://svelte.dev/tutorial/actions) with this library as well. The Svelte `use:` directive is not supported for Components, so you need to use the `use` prop instead:
+
+```svelte
+
+
+ (enabled = e.detail)}
+ use={[[action1, action1options], [action2], [action3, action3options]]}
+>
+ Toggle me!
+
+```
+
+The `use` prop must be a list of `[action]` or `[action, options]` items. These actions will be applied to the underlying element that the component eventually renders.
+
+## Customizing the elements rendered
+
+Each component in this library renders as some specific HTML element. By default, this element will be something that makes sense for that particular component: a `Label` component renders as ``, a `Button` component renders as a ``, etc. These defaults can be found on each component's page.
+
+You're free to change the element that any component renders as using the `as` prop, which is available on every component. This prop can take a string referring to an HTML element to use instead:
+
+```svelte
+
+
+
+ More
+
+
+
+
+ Account settings
+
+
+
+
+```
+
+Due to technical limitations in Svelte, each one of these elements must be handled individually by the library. Most elements should be supported, but some uncommon ones might not be. If you find yourself wanting to use an element that is _not_ currently supported, please [file a GitHub issue](https://github.com/rgossiaux/svelte-headlessui/issues).
diff --git a/src/routes/docs/index.svx b/src/routes/docs/index.svx
new file mode 100644
index 0000000..563f4d9
--- /dev/null
+++ b/src/routes/docs/index.svx
@@ -0,0 +1,17 @@
+Svelte Headless UI is an unofficial, complete Svelte port of the [Headless UI](https://headlessui.dev/) component library.
+
+These are high-quality, unstyled Svelte components which are:
+
+* Completely **unstyled**
+* Fully **accessible**
+* Extensively **tested**
+* Fully **typed** with TypeScript
+* SvelteKit compatible
+
+These components can serve as the basis for your own components or design system. They handle accessibility, keyboard and mouse interactions, focus management, and other things you would normally need to worry about when building your own components. You can simply add your own CSS and the rest will be managed for you.
+
+The original Headless UI library was written by Tailwind Labs and is maintained by them. This project is a direct port, but it is not affiliated with Tailwind Labs and is neither endorsed by, nor supported by, them; it is a community port.
+
+Headless UI was written originally to support the paid component library called [Tailwind UI](https://tailwindui.com/). This port was done with this use case in mind, and you can [use this with Tailwind UI](docs/tailwind-ui).
+
+This library works very well with [Tailwind CSS](https://tailwindcss.com/) as a styling system, but it is not a requirement & there is nothing Tailwind-specific in the library. [See here](docs/general-concepts#component-styling) for different methods to style these components with Svelte.
diff --git a/src/routes/docs/listbox.svx b/src/routes/docs/listbox.svx
new file mode 100644
index 0000000..8167bcc
--- /dev/null
+++ b/src/routes/docs/listbox.svx
@@ -0,0 +1,561 @@
+# Listbox
+
+
+
+
+
+## Basic example
+
+Listboxes are built using a combination of the `Listbox`, `ListboxButton`, `ListboxOptions`, `ListboxOption`, and `ListboxLabel` components.
+
+The `ListboxButton` will automatically open/close the `ListboxOptions` when clicked, and when the listbox is open, the list of items receives focus and is automatically navigable via the keyboard.
+
+```svelte
+
+
+ (selectedPerson = e.detail)}>
+ {selectedPerson.name}
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+```
+
+## Styling
+
+[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
+
+### Active and selected items
+
+To style the active `ListboxOption`, you can use the `active` slot prop that it provides, which tells you whether or not that option is currently focused via the mouse or keyboard.
+
+To style the selected `ListboxOption`, you can use the `selected` slot prop that it provides, which tells you whether or not that option is the selected option (the one passed to the `value` prop of the ``)
+
+You can use these states to conditionally apply whatever active/focus styles you wish.
+
+```svelte
+
+
+ (selectedPerson = e.detail)}>
+ {selectedPerson.name}
+
+ {#each people as person (person.id)}
+
+
+ (active ? "active" : "")}
+ let:selected
+ >
+ {#if selected}
+
+ {/if}
+ {person.name}
+
+ {/each}
+
+
+```
+
+## Using a custom label
+
+By default, the `Listbox` will use the `` contents as the label for screenreaders. If you'd like more control over what is announced to assistive technologies, use the `ListboxLabel` component:
+
+```svelte
+
+
+ (selectedPerson = e.detail)}>
+ Assignee:
+ {selectedPerson.name}
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+```
+
+## Showing/hiding the listbox
+
+By default, the `ListboxOptions` instance will be shown and hidden automatically based on the internal `open` state tracked by the `Listbox` component itself.
+
+If you'd rather handle this yourself (perhaps because you need to add an extra wrapper element for one reason or another), you can add a `static` prop to the `ListboxOptions` component to tell it to always render, and use the `open` slot prop provided by the `Listbox` to show or hide the listbox yourself.
+
+```svelte
+
+
+ (selectedPerson = e.detail)}
+ let:open
+>
+ {selectedPerson.name}
+ {#if open}
+
+
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+ {/if}
+
+```
+
+You can also choose to have the `Listbox` merely hide the `ListboxOptions` when the `Listbox` is closed, instead of removing it from the DOM entirely, by using the `unmount` prop. This may be useful for performance reasons if rendering the `ListboxOptions` is expensive.
+
+```svelte
+
+
+ (selectedPerson = e.detail)}>
+ {selectedPerson.name}
+
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+```
+
+## Disabling an item
+
+Use the `disabled` prop to disable a `ListboxOption`. This will make it unselectable via mouse and keyboard navigation, and it will be skipped when pressing the up/down arrows.
+
+```svelte
+
+
+ (selectedPerson = e.detail)}>
+ {selectedPerson.name}
+
+ {#each people as person (person.id)}
+
+
+ {person.name}
+
+
+ {/each}
+
+
+```
+
+## Transitions
+
+To animate the opening and closing of the listbox, you can use [this library's Transition component](/docs/transition) or Svelte's built-in transition engine. See that page for a comparison.
+
+### Using the `Transition` component
+
+To use the `Transition` component, all you need to do is wrap the `ListboxOptions` in a ``, and the transition will be applied automatically.
+
+```svelte
+
+
+ (selectedPerson = e.detail)}>
+ {selectedPerson.name}
+
+
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+
+```
+
+The components in this library communicate with each other, so the Transition will be managed automatically when the Listbox is opened/closed. If you require more control over this behavior, you may use a more explicit version:
+
+```svelte
+
+
+ (selectedPerson = e.detail)}
+ let:open
+>
+ {selectedPerson.name}
+
+
+
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+
+```
+
+### Using Svelte transitions
+
+The last example above also provides a blueprint for using Svelte transitions:
+
+```svelte
+
+
+ (selectedPerson = e.detail)}
+ let:open
+>
+ {selectedPerson.name}
+ {#if open}
+
+
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+ {/if}
+
+```
+
+Make sure to use the `static` prop, or else the exit transitions won't work correctly.
+
+## Horizontal options
+
+If you've styled your `ListboxOptions` to appear horizontally, use the `horizontal` prop on the `Listbox` component to enable navigating the items with the left and right arrow keys instead of up and down, and to update the `aria-orientation` attribute for assistive technologies.
+
+```svelte
+
+
+ (selectedPerson = e.detail)}
+ horizontal
+>
+ {selectedPerson.name}
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+```
+
+## Accessibility notes
+
+### Focus management
+
+When a Listbox is toggled open, the `ListboxOptions` receives focus. Focus is trapped within the list of items until Escape is pressed or the user clicks outside the options. Closing the `Listbox` returns focus to the `ListboxButton`.
+
+### Mouse interaction
+
+Clicking a `ListboxButton` toggles the options list open and closed. Clicking anywhere outside of the options list will close the listbox.
+
+### Keyboard interaction
+
+When the `horizontal` prop is set, the `` and `` below become `` and ``:
+
+| Command | Description |
+| ----------------------------------------------------------------------------------- | ----------------------------------------------- |
+| `` / `` / `` / `` when `ListboxButton` is focused | Opens listbox and focuses the selected item |
+| `` when listbox is open | Closes listbox |
+| `` / `` when listbox is open | Focuses next/previous non-disabled option |
+| `` / `` when listbox is open | Focuses first/last non-disabled option |
+| `` / `` when listbox is open | Selects the focused option |
+| `` when listbox is open | Focuses next option that matches keyboard input |
+
+### Other
+
+All relevant ARIA attributes are automatically managed.
+
+For a full reference on all accessibility features implemented in `Listbox`, see the ARIA spec on Listbox .
+
+## Component API
+
+### Listbox
+
+The main listbox component.
+
+| Prop | Default | Type | Description |
+| ------------ | ------- | --------- | ---------------------------------------------------------------------------------- |
+| `as` | `div` | `string` | The element the `Listbox` should render as |
+| `disabled` | `false` | `boolean` | Whether the entire `Listbox` and its children should be disabled |
+| `horizontal` | `false` | `boolean` | Whether the entire `Listbox` should be oriented horizontally instead of vertically |
+| `value` | -- | `T` | The selected value |
+
+| Slot prop | Type | Description |
+| ---------- | --------- | -------------------------------------- |
+| `disabled` | `boolean` | Whether or not the listbox is disabled |
+| `open` | `boolean` | Whether or not the listbox is open |
+
+This component also dispatches a custom event, which is listened to using the Svelte `on:` directive:
+
+| Event name | Type of event `.detail` | Description |
+| ---------- | ----------------------- | ------------------------------------------------------------------------------------------------------------- |
+| `change` | `T` | Dispatched when a `ListboxOption` is selected; the event `detail` contains the `value` of the selected option |
+
+### ListboxButton
+
+The listbox's button.
+
+| Prop | Default | Type | Description |
+| ---- | -------- | -------- | ------------------------------------------------ |
+| `as` | `button` | `string` | The element the `ListboxButton` should render as |
+
+| Slot prop | Type | Description |
+| ---------- | --------- | -------------------------------------- |
+| `disabled` | `boolean` | Whether or not the listbox is disabled |
+| `open` | `boolean` | Whether or not the listbox is open |
+
+### ListboxLabel
+
+A label that can be used for more control over the text your listbox will announce to screenreaders. Renders an element that is linked to the root `Listbox` via the `aria-labelledby` attribute and an autogenerated id.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ----------------------------------------------- |
+| `as` | `label` | `string` | The element the `ListboxLabel` should render as |
+
+| Slot prop | Type | Description |
+| ---------- | --------- | -------------------------------------- |
+| `disabled` | `boolean` | Whether or not the listbox is disabled |
+| `open` | `boolean` | Whether or not the listbox is open |
+
+### ListboxOptions
+
+The component that directly wraps the list of options in your custom listbox.
+
+| Prop | Default | Type | Description |
+| --------- | ------- | --------- | ----------------------------------------------------------------------------------------------- |
+| `as` | `ul` | `string` | The element the `ListboxOptions` should render as |
+| `static` | `false` | `boolean` | Whether the element should ignore the internally managed open/closed state |
+| `unmount` | `true` | `boolean` | Whether the element should be unmounted, instead of just hidden, based on the open/closed state |
+
+Note that `static` and `unmount` cannot be used together.
+
+| Slot prop | Type | Description |
+| --------- | --------- | ---------------------------------- |
+| `open` | `boolean` | Whether or not the listbox is open |
+
+### ListboxOption
+
+Used to wrap each item within your listbox.
+
+| Prop | Default | Type | Description |
+| ---------- | ------- | --------- | ------------------------------------------------------------------------------- |
+| `value` | -- | `T` | The option value |
+| `as` | `li` | `string` | The element the `ListboxOption` should render as |
+| `disabled` | `false` | `boolean` | Whether the option should be disabled for keyboard navigation and ARIA purposes |
+
+| Slot prop | Type | Description |
+| ---------- | --------- | -------------------------------------- |
+| `active` | `boolean` | Whether the option is active (focused) |
+| `selected` | `boolean` | Whether the option is selected |
+| `disabled` | `boolean` | Whether the option is disabled |
diff --git a/src/routes/docs/menu.svx b/src/routes/docs/menu.svx
new file mode 100644
index 0000000..d199d51
--- /dev/null
+++ b/src/routes/docs/menu.svx
@@ -0,0 +1,399 @@
+# Menu
+
+
+
+
+
+## Basic example
+
+Menus are built using a combination of the `Menu`, `MenuButton`, `MenuItems`, and `MenuItem` components:
+
+```svelte
+
+
+
+ More
+
+
+ Account settings
+
+
+ Documentation
+
+
+ Invite a friend (coming soon!)
+
+
+
+```
+
+## Styling
+
+[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
+
+### Active item
+
+To style the active `MenuItem`, you can use the `active` slot prop that it provides, which tells you whether or not that menu item is currently focused via the mouse or keyboard. You can use this state to conditionally apply whatever active/focus styles you wish.
+
+```svelte
+
+
+
+ More
+
+
+
+ Account settings
+
+
+
+
+```
+
+If necessary, you can also style the `MenuItem` itself by passing a function to `class`:
+
+```svelte
+
+
+
+ More
+
+
+ (active ? "active" : "inactive")}>
+ Account settings
+
+
+
+
+```
+
+## Showing/hiding the menu
+
+By default, the `MenuItems` instance will be shown and hidden automatically based on the internal `open` state tracked by the `Menu` component itself.
+
+If you'd rather handle this yourself (perhaps because you need to add an extra wrapper element for one reason or another), you can add a `static` prop to the `MenuItems` component to tell it to always render, and use the `open` slot prop provided by the `Menu` to show or hide the menu yourself.
+
+```svelte
+
+
+
+ More
+
+ {#if open}
+
+
+
+
+
+
+
+ {/if}
+
+```
+
+You can also choose to have the `Menu` merely hide the `MenuItems` when the `Menu` is closed, instead of removing it from the DOM entirely, by using the `unmount` prop. This may be useful for performance reasons if rendering the `MenuItems` is expensive.
+
+```svelte
+
+
+
+ More
+
+
+
+
+
+
+
+
+```
+
+## Disabling an item
+
+Use the `disabled` prop to disable a `MenuItem`. This will make it unselectable via keyboard navigation, and it will be skipped when pressing the up/down arrows.
+
+```svelte
+
+
+
+ More
+
+
+
+
+
+ Invite a friend (coming soon!)
+
+
+
+
+
+```
+
+## Transitions
+
+To animate the opening and closing of the menu, you can use [this library's Transition component](/docs/transition) or Svelte's built-in transition engine. See that page for a comparison.
+
+### Using the `Transition` component
+
+To use the `Transition` component, all you need to do is wrap the `MenuItems` in a ``, and the transition will be applied automatically.
+
+```svelte
+
+
+
+ More
+
+
+
+
+
+
+
+
+
+
+```
+
+The components in this library communicate with each other, so the Transition
+will be managed automatically when the Menu is opened/closed. If you require
+more control over this behavior, you may use a more explicit version:
+
+```svelte
+
+
+
+
+ More
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Using Svelte transitions
+
+The last example above also provides a blueprint for using Svelte transitions:
+
+```svelte
+
+
+
+
+ More
+
+ {#if open}
+
+
+
+
+
+
+
+
+
+ {/if}
+
+```
+
+Make sure to use the `static` prop, or else the exit transitions won't work correctly.
+
+## Rendering additional content
+
+The accessibility semantics of role="menu" are fairly strict, and any children of a Menu besides MenuItem components will be automatically hidden from assistive technology to make sure the menu works the way screen reader users expect.
+
+For this reason, rendering any children other than MenuItem components is discouraged, as that content will be inaccessible to people using assistive technology.
+
+If you want to build a dropdown with more flexible content, consider using Popover instead.
+
+## When to use a `Menu`
+
+Menus are best for UI elements that resemble things like the menus you'd find in the title bar of most operating systems. They have specific accessibility semantics, and their content should be restricted to a list of links or buttons. Focus is trapped in an open menu, so you cannot tab through the content or away from the menu. Instead, the arrow keys navigate through a `Menu`'s items.
+
+Here's when you might use other similar components from Headless UI:
+
+- Popover : Popovers are general-purpose floating menus.
+ They appear near the button that triggers them, and you can put arbitrary
+ markup in them like images or non-clickable content. The Tab key navigates the
+ contents of a Popover like it would any other normal markup. They're great for
+ building header nav items with expandable content and flyout panels.
+
+- Disclosure : Disclosures are useful for elements that
+ expand to reveal additional information, like a toggleable FAQ section. They
+ are typically rendered inline and reflow the document when they're shown or
+ hidden.
+
+- Dialog : Dialogs are meant to grab the user's full
+ attention. They typically render a floating panel in the center of the screen,
+ and use a backdrop to dim the rest of the application's contents. They also
+ capture focus and prevent tabbing away from the Dialog's contents until the
+ Dialog is dismissed.
+
+## Accessibility notes
+
+### Focus management
+
+Clicking the `MenuButton` toggles the menu and focuses the `MenuItems` component. Focus is trapped within the open menu until Escape is pressed or the user clicks outside the menu. Closing the menu returns focus to the `MenuButton`.
+
+### Mouse interaction
+
+Clicking a `MenuButton` toggles the menu. Clicking anywhere outside of an open menu will close that menu.
+
+### Keyboard interaction
+
+| Command | Description |
+| -------------------------------------------------------- | --------------------------------------------------- |
+| `` / `` when `MenuButton` is focused | Opens menu and focuses first non-disabled item |
+| `` / `` when `MenuButton` is focused | Opens menu and focuses first/last non-disabled item |
+| `` when menu is open | Closes any open Menus |
+| `` / `` when menu is open | Focuses next/previous non-disabled item |
+| `` / `` when menu is open | Focuses first/last non-disabled item |
+| `` / `` when menu is open | Activates/clicks the current menu item |
+| `` when menu is open | Focuses next item that matches keyboard input |
+
+### Other
+
+All relevant ARIA attributes are automatically managed.
+
+For a full reference on all accessibility features implemented in `Menu`, see the ARIA spec on Menus .
+
+## Component API
+
+### Menu
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | --------------------------------------- |
+| `as` | `div` | `string` | The element the `Menu` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | ------------------------------- |
+| `open` | `boolean` | Whether or not the menu is open |
+
+### MenuButton
+
+| Prop | Default | Type | Description |
+| ---- | -------- | -------- | --------------------------------------------- |
+| `as` | `button` | `string` | The element the `MenuButton` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | ------------------------------- |
+| `open` | `boolean` | Whether or not the menu is open |
+
+### MenuItems
+
+| Prop | Default | Type | Description |
+| --------- | ------- | --------- | ----------------------------------------------------------------------------------------------- |
+| `as` | `div` | `string` | The element the `MenuItems` should render as |
+| `static` | `false` | `boolean` | Whether the element should ignore the internally managed open/closed state |
+| `unmount` | `true` | `boolean` | Whether the element should be unmounted, instead of just hidden, based on the open/closed state |
+
+Note that `static` and `unmount` cannot be used together.
+
+| Slot prop | Type | Description |
+| --------- | --------- | ------------------------------- |
+| `open` | `boolean` | Whether or not the menu is open |
+
+### MenuItem
+
+| Prop | Default | Type | Description |
+| ---------- | ------- | --------- | ----------------------------------------------------------------------------- |
+| `as` | `a` | `string` | The element the `MenuItem` should render as |
+| `disabled` | `false` | `boolean` | Whether the item should be disabled for keyboard navigation and ARIA purposes |
+
+| Slot prop | Type | Description |
+| ---------- | --------- | ---------------------------------------------------------------------- |
+| `open` | `boolean` | Whether or not the menu is open |
+| `disabled` | `boolean` | Whether the item is disabled for keyboard navigation and ARIA purposes |
diff --git a/src/routes/docs/popover.svx b/src/routes/docs/popover.svx
new file mode 100644
index 0000000..ef5488a
--- /dev/null
+++ b/src/routes/docs/popover.svx
@@ -0,0 +1,428 @@
+# Popover
+
+
+
+
+
+## Basic example
+
+Popovers are built using the `Popover`, `PopoverButton`, and `PopverPanel` components.
+
+Clicking the `PopoverButton` will automatically open/close the `PopoverPanel`. When the panel is open, clicking anywhere outside of its contents, pressing the Escape key, or tabbing away from it will close the `Popover`.
+
+```svelte
+
+
+
+ Solutions
+
+
+
+
+
+
+
+
+
+```
+
+## Positioning the panel
+
+To get your popover to actually render a floating panel near your button, you'll need to use some styling technique that relies on CSS, JS, or both. In the previous example, we used CSS absolute and relative positioning so that the panel renders near the button that opened it.
+
+For more sophisticated approaches, you might use a library like Popper JS. We recommend using the [svelte-popperjs](https://github.com/bryanmylee/svelte-popperjs) wrapper and forwarding the actions to our components:
+
+```svelte
+
+
+
+ Solutions
+
+
+ Analytics
+ Engagement
+ Security
+ Integrations
+
+
+
+
+```
+
+## Styling
+
+[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
+
+## Showing/hiding the popover
+
+By default, your `PopoverPanel` will be shown/hidden automatically based on the internal open state tracked within the `Popover` component itself.
+
+If you'd rather handle this yourself (perhaps because you need to add an extra wrapper element for one reason or another), you can add a `static` prop to the `PopoverPanel` component to tell it to always render, and use the `open` slot prop provided by the `Popover` to show or hide the popover yourself.
+
+```svelte
+
+
+
+ Solutions
+ {#if open}
+
+ {/if}
+
+```
+
+## Closing popovers manually
+
+Since popovers can contain interactive content like form controls, we can't automatically close them when you click something inside of them like we can with `Menu` components.
+
+To close a popover manually when clicking a child of its panel, render that child as a `PopoverButton`. You can use the `as` prop to customize which element is being rendered.
+
+```svelte
+
+
+
+ Solutions
+
+ Insights
+
+
+
+```
+
+Alternatively, `Popover` and `PopoverPanel` expose a `close()` slot prop which you can use to imperatively close the panel:
+
+```svelte
+
+
+
+ Solutions
+
+ {
+ await fetch("/accept-terms", { method: "POST" });
+ close();
+ }}
+ >
+ Read and accept
+
+
+
+
+```
+
+By default the `PopoverButton` receives focus after calling `close()`, but you can change this by passing an element into `close(el)`.
+
+## Adding an overlay
+
+If you'd like to style a backdrop over your application UI whenever you open a `Popover`, use the `PopoverOverlay` component:
+
+```svelte
+
+
+
+ Solutions
+
+
+
+
+
+
+
+```
+
+Like all the other components, PopoverOverlay is completely unstyled, so how it appears is up to you. In this example, we put the `PopoverOverlay` before the `PopoverPanel` in the DOM so that it doesn't cover up the panel's contents. Also, since the `PopoverOverlay` is always rendered (even when the panel is closed), we use the `open` slot prop to only make it full-screen when the panel is open.
+
+## Transitions
+
+To animate the opening and closing of the popover panel, you can use [this library's Transition component](/docs/transition) or Svelte's built-in transition engine. See that page for a comparison.
+
+### Using the `Transition` component
+
+To use the `Transition` component, all you need to do is wrap the `PopoverPanel` in a `` and the panel will transition automatically.
+
+```svelte
+
+
+
+ Solutions
+
+
+
+
+
+
+
+```
+
+### Using Svelte transitions
+
+If you wish to animate your popovers using another technique (like Svelte's built-in transitions), you can use the `static` prop to tell the component to not manage rendering itself, so you can control it manually in another way.
+
+```svelte
+
+
+
+ Solutions
+ {#if open}
+
+ {/if}
+
+```
+
+Without the `static` prop, the exit transitions won't work correctly.
+
+## Grouping related popovers
+
+When rendering several related `Popover`s, for example in a site's header navigation, use the `PopoverGroup` component. This ensures panels stay open while users are tabbing between `Popover`s within a group, but closes any open panel once the user tabs outside of the group:
+
+```svelte
+
+
+
+
+ Solutions
+
+
+
+
+
+
+ Solutions
+
+
+
+
+
+```
+
+## When to use a `Popover`
+
+Here's how `Popover` compares to other components from Headless UI:
+
+- Menu : `Popover`s are more general-purpose than `Menu`s.
+ `Menu`s only support very restricted content and have specific accessibility
+ semantics. Arrow keys also navigate a `Menu`'s items, unlike a `Popover`.
+ `Menu`s are best for UI elements that resemble things like the menus you'd
+ find in the title bar of most operating systems. If your floating panel has
+ images or more markup than simple links, use a Popover.
+
+- Disclosure : `Disclosure`s are useful for things that
+ typically reflow the document, like an accordion. `Popover`s also have extra
+ behavior on top of `Disclosure`s: they can render overlays, and are closed
+ when the user either clicks the overlay (by clicking outside of the
+ `Popover`'s content) or presses the Escape key. If your UI element needs this
+ behavior, use a `Popover` instead of a `Disclosure`.
+
+- Dialog : `Dialog`s are meant to grab the user's full
+ attention. They typically render a floating panel in the center of the screen,
+ and use a backdrop to dim the rest of the application's contents. They also
+ capture focus and prevent tabbing away from the `Dialog`'s contents until the
+ `Dialog` is dismissed. Popovers are more contextual, and are usually
+ positioned near the element that triggered them.
+
+## Accessibility notes
+
+### Focus management
+
+Pressing Tab on an open panel will focus the first focusable element within the panel's contents. If a `PopoverGroup` is being used, Tab cycles from the end of an open panel's content to the next `Popover`'s button.
+
+### Mouse interaction
+
+Clicking a `PopoverButton` toggles a panel open and closed. Clicking anywhere outside of an open panel will close that panel.
+
+### Keyboard interaction
+
+| Command | Description |
+| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `` / `` when a `PopoverButton` is focused | Toggles panel |
+| `` | Closes any open popovers |
+| `` | Cycles through an open panel's contents. Tabbing out of an open panel will close that panel, and tabbing from one open panel to a sibling popover's button (within a `PopoverGroup`) closes the first panel |
+| `` + `` | Cycles backwards through an open panel's contents |
+
+### Other
+
+Nested `Popover`s are supported, and all panels will close correctly whenever the root panel is closed.
+
+All relevant ARIA attributes are automatically managed.
+
+## Component API
+
+### Popover
+
+The main popover component.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ------------------------------------------ |
+| `as` | `div` | `string` | The element the `Popover` should render as |
+
+| Slot prop | Type | Description |
+| --------- | ---------------------------- | ----------------------------------------------------------------------------- |
+| `open` | `boolean` | Whether the popover is open |
+| `close` | `(el?: HTMLElement) => void` | Closes the popover and focuses `el`, if passed, or the `PopoverButton` if not |
+
+### PopoverOverlay
+
+This can be used to create an overlay for your popover. Clicking on the overlay will close the popover.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ------------------------------------------------- |
+| `as` | `div` | `string` | The element the `PopoverOverlay` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | ---------------------------------- |
+| `open` | `boolean` | Whether or not the popover is open |
+
+### PopoverButton
+
+This is the trigger button to toggle a popover.
+
+You can also use this `PopoverButton` component inside a `PopoverPanel`. If you do, it will behave as a close button and have the appropriate `aria-*` attributes.
+
+| Prop | Default | Type | Description |
+| ---- | -------- | -------- | ------------------------------------------------ |
+| `as` | `button` | `string` | The element the `PopoverButton` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | ---------------------------------- |
+| `open` | `boolean` | Whether or not the popover is open |
+
+### PopoverPanel
+
+This component contains the contents of your popover.
+
+| Prop | Default | Type | Description |
+| --------- | ------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `as` | `div` | `string` | The element the `PopoverPanel` should render as |
+| `focus` | `false` | `boolean` | Whether the `PopoverPanel` should trap focus. If `true`, focus will move inside the `PopoverPanel` when it is opened, and if focus leaves the `PopoverPanel` it will close. |
+| `static` | `false` | `boolean` | Whether the element should ignore the internally managed open/closed state |
+| `unmount` | `true` | `boolean` | Whether the element should be unmounted, instead of just hidden, based on the open/closed state |
+
+Note that `static` and `unmount` cannot be used together.
+
+| Slot prop | Type | Description |
+| --------- | ---------------------------- | ----------------------------------------------------------------------------- |
+| `open` | `boolean` | Whether or not the popover is open |
+| `close` | `(el?: HTMLElement) => void` | Closes the popover and focuses `el`, if passed, or the `PopoverButton` if not |
+
+### PopoverGroup
+
+This component links related `Popover`s. Tabbing out of one `PopoverPanel` will focus the next popover's `PopverButton`, and tabbing outside of the `PopoverGroup` completely will close all popovers inside the group.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ----------------------------------------------- |
+| `as` | `div` | `string` | The element the `PopoverGroup` should render as |
diff --git a/src/routes/docs/radio-group.svx b/src/routes/docs/radio-group.svx
new file mode 100644
index 0000000..d78b9cd
--- /dev/null
+++ b/src/routes/docs/radio-group.svx
@@ -0,0 +1,240 @@
+# Radio Group
+
+
+
+
+
+## Basic example
+
+Radio Groups are built using a combination of the `RadioGroup`, `RadioGroupOption`, `RadioGroupLabel`, and `RadioGroupDescription` components:
+
+```svelte
+
+
+ (plan = e.detail)}>
+ Plan
+
+ Startup
+
+
+ Business
+
+
+ Enterprise
+
+
+
+
+```
+
+## Styling
+
+[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
+
+### Checked option
+
+To style the selected `RadioGroupOption`, you can use the `checked` slot prop that it provides, which tells you whether or not that option is the currently selected one. You can use this state to conditionally apply whatever styles you wish.
+
+```svelte
+
+
+ (plan = e.detail)}>
+ Plan
+
+
+ Startup
+
+
+
+
+
+```
+
+### Active options
+
+`RadioGroupOption`s can also be in an `active` state if they are focused with either the mouse or the keyboard. It's a good idea to add styles specifically for this state.
+
+```svelte
+
+
+ (plan = e.detail)}>
+ Plan
+
+
+ Startup
+
+
+
+
+
+```
+
+## Using the Label and Description components
+
+You can use the `RadioGroupLabel` and `RadioGroupDescription` components to label and describe each `RadioGroupOption`, as well as the `RadioGroup` itself. These components will automatically link with their relevant ancestor components via the `aria-labelledby` and `aria-describedby` attributes, improving the semantics and accessibility of your component.
+
+By default, `RadioGroupLabel` renders a `` element and `RadioGroupDescription` renders a ``. These can be customized using the `as` prop, as described in the API docs below.
+
+```svelte
+
+
+ plan = e.detail}>
+
+ Plan
+
+
+ Startup
+
+ Up to 5 active job postings
+
+
+
+
+
+
+```
+
+## Accessibility notes
+
+### Mouse interaction
+
+Clicking a `RadioGroupOption` will select it.
+
+### Keyboard interaction
+
+| Command | Description |
+| ----------------------------------------------------------- | ------------------------------------------------------------------ |
+| `` / `` when `RadioGroup` is focused | Focuses and checks the next `RadioGroupOption` |
+| `` / `` when `RadioGroup` is focused | Focuses and checks the previous `RadioGroupOption` |
+| `` when `RadioGroup` is focused | Checks the current `RadioGroupOption` if it is not already checked |
+
+### Other
+
+All relevant ARIA attributes are automatically managed.
+
+For a full reference on all accessibility features implemented in `RadioGroup`, see the ARIA spec on Radio Groups .
+
+## Component API
+
+### RadioGroup
+
+The main Radio Group component.
+
+| Prop | Default | Type | Description |
+| ---------- | ------- | ------------------ | ------------------------------------------------------------------------ |
+| `as` | `div` | `string` | The element the `RadioGroup` should render as |
+| `disabled` | `false` | `boolean` | Whether the `RadioGroup` and all of its `RadioGroupOption`s are disabled |
+| `value` | -- | `T` \| `undefined` | The currently selected value in the `RadioGroup` |
+
+This component also dispatches a custom event, which is listened to using the Svelte `on:` directive:
+
+| Event name | Type of event `.detail` | Description |
+| ---------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------- |
+| `change` | `T` | Dispatched when a `RadioGroupOption` is selected; the event `detail` contains the `value` of the selected option |
+
+### RadioGroupOption
+
+The wrapper component for each selectable option.
+
+| Prop | Default | Type | Description |
+| ---------- | ------- | ------------------ | ----------------------------------------------------------------------------------------------------------- |
+| `as` | `div` | `string` | The element the `RadioGroupOption` should render as |
+| `disabled` | `false` | `boolean` | Whether the `RadioGroupOption` is disabled |
+| `value` | -- | `T` \| `undefined` | The value of the `RadioGroupOption`; the type should match the type of the `value` prop in the `RadioGroup` |
+
+| Slot prop | Type | Description |
+| ---------- | --------- | ---------------------------------------------------------- |
+| `active` | `boolean` | Whether the option is active (using the mouse or keyboard) |
+| `checked` | `boolean` | Whether the option is the checked option |
+| `disabled` | `boolean` | Whether the option is disabled |
+
+### RadioGroupLabel
+
+Renders an element that is linked to its nearest `RadioGroup` or `RadioGroupOption` ancestor component via the `aria-labelledby` attribute and an autogenerated id.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | -------------------------------------------------- |
+| `as` | `label` | `string` | The element the `RadioGroupLabel` should render as |
+
+If the `RadioGroupLabel` is labeling a `RadioGroupOption` (instead of the `RadioGroup`), it will also have these slot props available:
+
+| Slot prop | Type | Description |
+| ---------- | --------- | ------------------------------------------------------------------------ |
+| `active` | `boolean` | Whether the corresponding option is active (using the mouse or keyboard) |
+| `checked` | `boolean` | Whether the corresponding option is the checked option |
+| `disabled` | `boolean` | Whether the corresponding option is disabled |
+
+### RadioGroupDescription
+
+Renders an element that is linked to its nearest `RadioGroup` or `RadioGroupOption` ancestor component via the `aria-describedby` attribute and an autogenerated id.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | -------------------------------------------------------- |
+| `as` | `a` | `string` | The element the `RadioGroupDescription` should render as |
+
+If the `RadioGroupDescription` is describing a `RadioGroupOption` (instead of the `RadioGroup`), it will also have these slot props available:
+
+| Slot prop | Type | Description |
+| ---------- | --------- | ------------------------------------------------------------------------ |
+| `active` | `boolean` | Whether the corresponding option is active (using the mouse or keyboard) |
+| `checked` | `boolean` | Whether the corresponding option is the checked option |
+| `disabled` | `boolean` | Whether the corresponding option is disabled |
diff --git a/src/routes/docs/switch.svx b/src/routes/docs/switch.svx
new file mode 100644
index 0000000..5c717d5
--- /dev/null
+++ b/src/routes/docs/switch.svx
@@ -0,0 +1,318 @@
+# Switch
+
+
+
+
+
+## Basic example
+
+Switches are built using the `Switch` component. You can toggle your switch by clicking directly on the component or by pressing the spacebar while it's focused.
+
+Toggling the switch fires the `change` event with a negated version of the `checked` value.
+
+```svelte
+
+
+ (enabled = e.detail)}
+ class={enabled ? "switch switch-enabled" : "switch switch-disabled"}
+>
+ Enable notifications
+
+
+
+
+```
+
+## Styling
+
+[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
+
+## Using a custom label
+
+By default, the `Switch` renders a `` as well as whatever children you pass into it. This can make it harder to implement certain UIs, since the children will be nested within the button.
+
+In these situations, you can use the `SwitchGroup` component for more flexibility.
+
+This example demonstrates how to use the `SwitchGroup`, `Switch`, and `SwitchLabel` components to render a label as a sibling to a button. Note that `SwitchLabel` is used alongside a `Switch`, and both must be rendered within a parent `SwitchGroup`.
+
+```svelte
+
+
+
+
+ Enable notifications
+ (enabled = e.detail)}
+ class={enabled ? "switch switch-enabled" : "switch switch-disabled"}
+ >
+ Enable notifications
+
+
+
+
+
+
+```
+
+By default, clicking a `SwitchLabel` will toggle the switch, just like labels in native HTML checkboxes do. If you'd like to make the label non-clickable (which you might if it doesn't make sense for your design), you can add a `passive` prop to the `SwitchLabel` component:
+
+```svelte
+
+
+
+ Enable notifications
+ (enabled = e.detail)}>
+
+
+
+```
+
+## Transitions
+
+Because switches are always rendered to the DOM (rather than being mounted/unmounted like other components in the library), there's generally no need to use the provided `Transition` component. You can just use CSS transitions or Svelte's transition directives:
+
+```svelte
+
+
+ (enabled = e.detail)}>
+
+
+
+
+
+```
+
+## Accessibility notes
+
+### Labels
+
+By default, the children of a `Switch` will be used as the label for screen readers. If you're using `SwitchLabel`, the content of your `Switch` component will be ignored by assistive technologies.
+
+### Mouse interaction
+
+Clicking a `Switch` or a `SwitchLabel` toggles the switch on and off.
+
+### Keyboard interaction
+
+When the `horizontal` prop is set, the `` and `` below become `` and ``:
+
+| Command | Description |
+| ------------------------------------ | ------------------ |
+| `` when a `Switch` is focused | Toggles the switch |
+
+### Other
+
+All relevant ARIA attributes are automatically managed.
+
+For a full reference on all accessibility features implemented in `Switch`, see the ARIA spec on Switch .
+
+## Component API
+
+### Switch
+
+The main switch component.
+
+| Prop | Default | Type | Description |
+| --------- | -------- | --------- | ----------------------------------------- |
+| `as` | `button` | `string` | The element the `Switch` should render as |
+| `checked` | `false` | `boolean` | Whether the switch is checked |
+
+| Slot prop | Type | Description |
+| --------- | --------- | ----------------------------- |
+| `checked` | `boolean` | Whether the switch is checked |
+
+This component also dispatches a custom event, which is listened to using the Svelte `on:` directive:
+
+| Event name | Type of event `.detail` | Description |
+| ---------- | ----------------------- | ---------------------------------------------------------------------------------------------- |
+| `change` | `T` | Dispatched when a `Switch` is toggled; the event `detail` contains the new value of the switch |
+
+### SwitchLabel
+
+A label that can be used for more control over the text your switch will announce to screenreaders. Renders an element that is linked to the `Switch` via the `aria-labelledby` attribute and an autogenerated id.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ---------------------------------------------- |
+| `as` | `label` | `string` | The element the `SwitchLabel` should render as |
+
+### SwitchDescription
+
+This is the description for your switch. When this is used, it will set the `aria-describedby` on the switch.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ---------------------------------------------------- |
+| `as` | `p` | `string` | The element the `SwitchDescription` should render as |
+
+| Slot prop | Type | Description |
+| --------- | --------- | --------------------------------- |
+| `open` | `boolean` | Whether or not the switch is open |
+
+### SwitchGroup
+
+Used to wrap a `Switch` together with a `SwitchLabel` and/or `SwitchDescription`
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ---------------------------------------------- |
+| `as` | `div` | `string` | The element the `SwitchGroup` should render as |
diff --git a/src/routes/docs/tabs.svx b/src/routes/docs/tabs.svx
new file mode 100644
index 0000000..0c3af6c
--- /dev/null
+++ b/src/routes/docs/tabs.svx
@@ -0,0 +1,337 @@
+# Tabs
+
+
+
+
+
+## Basic example
+
+Tabs are built using the `TabGroup`, `TabList`, `Tab`, `TabPanels`, and `TabPanel` components.
+
+By default the first tab is selected, and clicking on any tab or selecting it with the keyboard will activate the corresponding panel.
+
+```svelte
+
+
+
+
+ Tab 1
+ Tab 2
+ Tab 3
+
+
+ Content 1
+ Content 2
+ Content 3
+
+
+```
+
+## Styling
+
+[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
+
+### The selected tab
+
+To style the active `Tab`, you can use the `selected` slot prop that it provides, which tells you whether or not that tab is currently selected. You can also access `selected` as an argument to a function passed to `class` ([see here](general-concepts#styling-the-components-themselves-class-and-style)). You can use this state to conditionally apply whatever styles you wish.
+
+```svelte
+
+
+
+
+ "tab-selected" : "tab-unselected"}>Tab 1
+
+
+
+ Content 1
+
+
+
+
+
+```
+
+## Disabling a tab
+
+To disable a tab, use the `disabled` prop on the `Tab` component. Disabled tabs cannot be selected with the mouse, and are also skipped when navigating the tab list using the keyboard.
+
+```svelte
+
+
+
+
+ Tab 1
+ Tab 2
+ Tab 3
+
+
+ Content 1
+ Content 2
+ Content 3
+
+
+```
+
+## Manually activating tabs
+
+By default, tabs are automatically selected as the user navigates through them using the arrow keys.
+
+If you'd rather not change the current tab until the user presses Enter or Space, use the `manual` prop on the `TabGroup` component. This can be helpful if selecting a tab performs an expensive operation and you don't want to run it unnecessarily.
+
+```svelte
+
+
+
+
+ Tab 1
+ Tab 2
+ Tab 3
+
+
+ Content 1
+ Content 2
+ Content 3
+
+
+```
+
+The `manual` prop has no impact on mouse interactions; tabs will still be selected as soon as they are clicked.
+
+## Vertical tabs
+
+If you've styled your `TabList` to appear vertically, use the `vertical` prop to enable navigating with the up and down arrow keys instead of with the left and right arrows, and to update the `aria-orientation` attribute for assistive technologies.
+
+```svelte
+
+
+
+
+ Tab 1
+ Tab 2
+ Tab 3
+
+
+ Content 1
+ Content 2
+ Content 3
+
+
+```
+
+## Specifying the default tab
+
+To change which tab is selected by default, use the `defaultIndex={number}` prop on the `TabGroup` component.
+
+```svelte
+
+
+
+
+
+ Tab 1
+
+
+ Tab 2
+
+ Tab 3
+
+
+ Content 1
+
+
+ Content 2
+
+ Content 3
+
+
+```
+
+If you happen to provide an index that is out of bounds, then the last non-disabled tab will be selected on initial render. For example, `` in the above example would select Tab 3.
+
+## Listening for changes
+
+To run a function whenever the selected tab changes, use the `change` event on the `TabGroup` component.
+
+```svelte
+
+
+ console.log("Changed selected tab to:", e.detail)}>
+
+ Tab 1
+ Tab 2
+ Tab 3
+
+
+ Content 1
+ Content 2
+ Content 3
+
+
+```
+
+## Accessibility notes
+
+### Mouse interaction
+
+Clicking a `Tab` will select that tab and display the corresponding `TabPanel`.
+
+### Keyboard interaction
+
+All interactions apply when a `Tab` component is focused.
+
+| Command | Description |
+| -------------------------------------------------- | ------------------------------------------ |
+| `` / `` | Selects the previous/next non-disabled tab |
+| `` / `` when `vertical` is set | Selects the previous/next non-disabled tab |
+| `` / `` | Selects the first non-disabled tab |
+| `` / `` | Selects the last non-disabled tab |
+| `` / `` when `manual` is set | Activates the selected tab |
+
+### Other
+
+All relevant ARIA attributes are automatically managed.
+
+For a full reference on all accessibility features implemented in `Tab`, see the ARIA spec on Tabs .
+
+## Component API
+
+### TabGroup
+
+The main tab group component.
+
+| Prop | Default | Type | Description |
+| -------------- | ------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `as` | `div` | `string` | The element the `TabGroup` should render as |
+| `defaultIndex` | `0` | `number` | The index of the default selected tab |
+| `vertical` | `false` | `boolean` | Whether the orientation of the `TabList` is vertical instead of horizontal |
+| `manual` | `false` | `boolean` | Whether, in keyboard navigation, the `Enter` or `Space` key is necessary to change tabs. By default, the arrow keys will change tabs automatically without hitting `Enter`/`Space`. This has no impact on mouse behavior |
+
+| Slot prop | Type | Description |
+| --------------- | -------- | ---------------------------- |
+| `selectedIndex` | `number` | The currently selected index |
+
+This component also dispatches a custom event, which is listened to using the Svelte `on:` directive:
+
+| Event name | Type of event `.detail` | Description |
+| ---------- | ----------------------- | --------------------------------------- |
+| `change` | `number` | Emitted whenever the active tab changes |
+
+### TabList
+
+The container for `Tab` components. The order of the `Tab` components it contains must correspond to the order of the `TabPanels`.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ------------------------------------------ |
+| `as` | `div` | `string` | The element the `TabList` should render as |
+
+| Slot prop | Type | Description |
+| --------------- | -------- | ---------------------------- |
+| `selectedIndex` | `number` | The currently selected index |
+
+### Tab
+
+This component wraps the selector for an individual tab. All `Tab`s will be rendered at once.
+
+| Prop | Default | Type | Description |
+| ---------- | -------- | --------- | --------------------------------------- |
+| `as` | `button` | `string` | The element the `Tab` should render as |
+| `disabled` | `false` | `boolean` | Whether the `Tab` is currently disabled |
+
+| Slot prop | Type | Description |
+| ---------- | --------- | --------------------------------------- |
+| `selected` | `boolean` | Whether the `Tab` is currently selected |
+
+### TabPanels
+
+The container for `TabPanel` components. The order of the `TabPanel` components it contains must correspond to the order of the `TabList`.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | -------------------------------------------- |
+| `as` | `div` | `string` | The element the `TabPanels` should render as |
+
+| Slot prop | Type | Description |
+| --------------- | -------- | ---------------------------- |
+| `selectedIndex` | `number` | The currently selected index |
+
+### TabPanel
+
+This component wraps the contents of an individual tab. Only one `TabPanel` will be visible at once.
+
+| Prop | Default | Type | Description |
+| ---- | ------- | -------- | ------------------------------------------- |
+| `as` | `div` | `string` | The element the `TabPanel` should render as |
+
+| Slot prop | Type | Description |
+| ---------- | --------- | --------------------------------------------------- |
+| `selected` | `boolean` | Whether or not the `TabPanel` is currently selected |
diff --git a/src/routes/docs/tailwind-ui.svx b/src/routes/docs/tailwind-ui.svx
new file mode 100644
index 0000000..50e4ced
--- /dev/null
+++ b/src/routes/docs/tailwind-ui.svx
@@ -0,0 +1,284 @@
+# Using Tailwind UI
+
+The original Headless UI libraries for React & Vue were made by Tailwind Labs in order to serve as a foundation for [Tailwind UI](https://tailwindui.com/), a paid set of component templates. While this Svelte port has no affiliation with Tailwind Labs or Tailwind UI, one of the main motivations behind it is to make it easy to use Tailwind UI with Svelte. This page contains tips and instructions for converting their provided snippets to work with Svelte.
+
+You can convert either the React or the Vue templates to Svelte, if you are more familiar with one or the other framework. If you are unfamiliar with both React and Vue, below are some more comprehensive instructions for converting the React snippets.
+
+## General concerns
+
+### Heroicons
+
+Many Tailwind UI examples use the [heroicons](https://heroicons.com/) icon library. This library has official wrappers for React and Vue, but not for Svelte. For Svelte, you can use the wrapper library [@rgossiaux/svelte-heroicons](https://github.com/rgossiaux/svelte-heroicons), which provides the same API and component names as the official Tailwind wrappers, making it faster to convert Tailwind UI code to Svelte. Of course, you are free to use any other library you wish instead, if you prefer.
+
+## Differences from React
+
+### Component names
+
+The React library uses a `.` in the names of many components: `Tab.Group`, `Listbox.Button`, etc. The Svelte library does not use this pattern and instead exports every component under its own name with no dot: `TabGroup`, `ListboxButton`, etc.
+
+### `useState`
+
+State declared with `useState` in React becomes just a normal variable in Svelte:
+
+```jsx
+import { useState } from 'react'
+import { Switch } from '@headlessui/react'
+
+function MyToggle() {
+ const [enabled, setEnabled] = useState(false)
+
+ return (
+
+ // ...
+
+ )
+}
+```
+
+becomes
+
+```svelte
+
+
+ enabled = e.detail}>
+
+
+```
+
+### JSX camelCased attribute names
+
+In React, some HTML attributes have different names from the standard ones that are used in Svelte. These are covered in the [React documentation](https://reactjs.org/docs/dom-elements.html#differences-in-attributes), but we repeat the most important differences here:
+
+* `className` in React becomes `class` in Svelte
+* `htmlFor` in React becomes `for` in Svelte
+* SVG attributes in React like `strokeWidth`, `strokeLinecap`, etc. become `stroke-width`, `stroke-linecap`, etc. in Svelte
+
+### Event handlers
+
+In React, you pass event handlers using camelCased names:
+
+```jsx
+import { useState } from 'react'
+import { Dialog } from '@headlessui/react'
+
+function MyDialog() {
+ let [isOpen, setIsOpen] = useState(true)
+
+ return (
+ setIsOpen(false)}>
+
+
+ Deactivate account
+
+ This will permanently deactivate your account
+
+
+
+ Are you sure you want to deactivate your account? All of your data will
+ be permanently removed. This action cannot be undone.
+
+
+ setIsOpen(false)}>Deactivate
+ setIsOpen(false)}>Cancel
+
+ )
+}
+```
+
+In Svelte, you instead use the `on:` directive:
+
+```svelte
+
+
+ (isOpen = false)}>
+
+
+ Deactivate account
+
+ This will permanently deactivate your account
+
+
+
+ Are you sure you want to deactivate your account? All of your data will be
+ permanently removed. This action cannot be undone.
+
+
+ (isOpen = false)}>Deactivate
+ (isOpen = false)}>Cancel
+
+```
+
+Furthermore, in the React library, event handlers will be called with the their data directly:
+
+```jsx
+
+ // ...
+
+```
+
+In the Svelte version, your handler will be passed a `CustomEvent` object, and you need to look at its `.detail` property:
+```svelte
+ selectedPerson = e.detail}>
+
+
+```
+
+### Render props
+
+The React components make use of render props:
+
+```jsx
+
+ {({ close }) => (
+ {
+ await fetch('/accept-terms', { method: 'POST' });
+ close();
+ }}
+ >
+ Read and accept
+
+ )}
+
+```
+
+The Svelte equivalent of this pattern is to use [slot props](https://svelte.dev/tutorial/slot-props):
+
+```svelte
+
+ {
+ await fetch('/accept-terms', { method: 'POST' })
+ close()
+ }}
+ >
+ Read and accept
+
+
+```
+
+### Conditional rendering
+
+The standard way to do conditional rendering in React is with the `&&` operator:
+
+```jsx
+
+ {({ open }) => (
+ <>
+ Is team pricing available?
+
+ {open && (
+
+ {/*
+ Using `static`, `Disclosure.Panel` is always rendered and
+ ignores the `open` state.
+ */}
+ {/* ... */}
+
+ )}
+ >
+ )}
+
+```
+
+In Svelte, you use the `{#if}` templating syntax instead:
+
+```svelte
+
+ Is team pricing available?
+
+ {#if open}
+
+
+
+
+
+ {/if}
+
+```
+
+### Iteration / mapping
+
+Tailwind UI frequenty does iteration using `Array.prototype.map()`:
+
+```jsx
+
+ {selectedPerson.name}
+
+ {people.map((person) => (
+
+ {person.name}
+
+ ))}
+
+
+```
+
+In Svelte, you accomplish this by using the `{#each}` template syntax:
+
+```svelte
+ selectedPerson = e.detail}>
+ {selectedPerson.name}
+
+ {#each people as person (person.id)}
+
+ {person.name}
+
+ {/each}
+
+
+```
+
+Note that the key moves from the `key=` prop in React into the `{#each}` statement in Svelte. Don't forget to add this!
+
+### Using a dynamic component
+
+In React, you can use a variable as a tag name:
+
+```jsx
+{solutions.map((item) => (
+ // ...
+
+ // ...
+))}
+```
+
+In Svelte, you use `` instead:
+
+```jsx
+{#each solutions as item}
+
+
+
+{/each}
+```
+
+### Fragments
+
+You may see the empty `<>` fragment tag in Tailwind UI snippets. You can generally simply delete this in Svelte.
+
+You may also see the `as={Fragment}` prop on some components. In the React library, you can choose render a component as a fragment, meaning that it will not render anything at all and will instead set props on its *child* component or element. This is currently impossible in Svelte, so every component in this library must always render an element. When you see `as={Fragment}`, you should just delete it.
+
+Unfortunately, it some cases this can cause some visual differences. For example, templates that use z-index might require copying some classes into the component (usually a transition) that used to have `as={Fragment}`, due to changes in the [stacking context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context).
+
+Modal components that have `{/* This element is to trick the browser into centering the modal contents. */}` above a `Transition` might require moving that element inside the `Transition`.
+
+If you run into problems related to this that aren't mentioned here, please [report them on GitHub](https://github.com/rgossiaux/svelte-headlessui/issues) so that we can improve the documentation.
diff --git a/src/routes/docs/transition.svx b/src/routes/docs/transition.svx
new file mode 100644
index 0000000..ad5b219
--- /dev/null
+++ b/src/routes/docs/transition.svx
@@ -0,0 +1,242 @@
+# Transition
+
+
+
+
+
+## About this component
+
+One of Svelte's strengths is its great support for transitions and animations. You are welcome and even encouraged to use them with this library when possible. So why bother with the `Transition` component?
+
+There are a few reasons you may choose to use `Transition`:
+
+- You are converting code from Tailwind UI which uses a `Transition` component. It may be simpler to just be able to port that code directly instead of replacing it with a Svelte transition.
+
+- You want to use CSS class-based transitions (perhaps via Tailwind CSS classes, perhaps not) instead of using Svelte's transition engine. In that case this component can help, especially by helping coordinate multiple transitions (one of the main features of the component).
+
+Since this component works is based on CSS class, we'll be using Tailwind CSS transition classes for the examples. However, you are free to use your own transition classes, as long as they are globally available.
+
+## Basic example
+
+The `Transition` accepts a `show` prop that controls whether the children should be shown or hidden, and a set of lifecycle props (like `enterFrom` and `leaveTo`) that let you add CSS classes at specific phases of a transition.
+
+```svelte
+
+
+ (show = !show)}> Toggle
+
+ I will fade in and out
+
+```
+
+## Showing and hiding content
+
+Wrap the content that should be conditionally rendered in a `Transition` component, and use the `show` prop to control whether the content should be visible or hidden.
+
+```svelte
+
+
+ (show = !show)}> Toggle
+I will appear and disappear
+```
+
+The `Transition` component will render a `div` by default, but you can use the `as` prop to render a different element instead if needed. Any other HTML attributes (like `class` or `style`) can be added directly to the `Transition` the same way they would be for regular HTML elements.
+
+```svelte
+
+
+ (show = !show)}> Toggle
+
+ I will appear and disappear
+
+```
+
+## Animating transitions
+
+By default, a `Transition` will enter and leave instantly, which is probably not what you're looking for if you're using this component.
+
+To animate your enter/leave transitions, add classes that provide the styling for each phase of the transitions using these props:
+
+- `enter`: Applied the entire time an element is entering. Usually you define your duration and what properties you want to transition here: `transition-opacity duration-75`, for example.
+
+- `enterFrom`: The starting point to enter from, like `opacity-0` if something should fade in.
+
+- `enterTo`: The ending point to enter to, like `opacity-100` after fading in.
+
+- `leave`: Applied the entire time an element is leaving. Usually you define your duration and what properties you want to transition here: `transition-opacity duration-75`, for example.
+
+- `leaveFrom`: The starting point to leave from, like `opacity-100` if something should fade out.
+
+- `leaveTo`: The ending point to leave to, like `opacity-0` after fading out.
+
+Here's an example:
+
+```svelte
+
+
+ (show = !show)}> Toggle
+
+ I will fade in and out
+
+```
+
+In this example, the transitioning element will take 75ms to enter (that's the `duration-75` class), and will transition the opacity property during that time (that's `transition-opacity`).
+
+It will start completely transparent before entering (that's `opacity-0` in the `enterFrom` phase), and fade in to completely opaque (`opacity-100`) when finished (that's the `enterTo` phase).
+
+When the element is being removed (the `leave` phase), it will transition the opacity property, and spend 150ms doing it (`transition-opacity duration-150`).
+
+It will start as completely opaque (the `opacity-100` in the `leaveFrom` phase), and finish as completely transparent (the `opacity-0` in the `leaveTo` phase).
+
+All of these props are optional, and will default to just an empty string.
+
+## Coordinating multiple transitions
+
+Sometimes you need to transition multiple elements with different animations but all based on the same state. For example, say the user clicks a button to open a sidebar that slides over the screen, and you also need to fade-in a background overlay at the same time.
+
+You can do this by wrapping the related elements with a parent `Transition` component, and wrapping each child that needs its own transition styles with a `TransitionChild` component, which will automatically communicate with the parent `Transition` and inherit the parent's `show` state.
+
+```svelte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+The `TransitionChild` component has the exact same API as the `Transition` component, but with no `show` prop, since the `show` value is controlled by the parent.
+
+Parent `Transition` components will always automatically wait for all children to finish transitioning before unmounting, so you don't need to manage any of that timing yourself.
+
+## Transitioning on initial mount
+
+If you want an element to transition the very first time it's rendered, set the `appear` prop to `true`.
+
+This is useful if you want something to transition in on initial page load, or when its parent is conditionally rendered.
+
+```svelte
+
+
+
+
+
+```
+
+## Component API
+
+### Transition
+
+| Prop | Default | Type | Description |
+| ----------- | ------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------- |
+| `show` | -- | `boolean` | Whether the children should be shown or hidden |
+| `as` | `div` | `string` | The element the `Transition` should render as |
+| `static` | `false` | `boolean` | Whether the element should ignore the internally managed open/closed state |
+| `unmount` | `true` | `boolean` | Whether the element should be unmounted, instead of just hidden, based on the open/closed state |
+| `enter` | `""` | `string` | Classes to add to the transitioning element during the entire enter phase |
+| `enterFrom` | `""` | `string` | Classes to add to the transitioning element before the enter phase starts |
+| `enterTo` | `""` | `string` | Classes to add to the transitioning element immediately after the enter phase starts |
+| `entered` | `""` | `string` | Classes to add to the transitioning element once the transition is done. These classes will persist after that until the `leave` phase |
+| `leave` | `""` | `string` | Classes to add to the transitioning element during the entire leave phase |
+| `leaveFrom` | `""` | `string` | Classes to add to the transitioning element before the leave phase starts |
+| `leaveTo` | `""` | `string` | Classes to add to the transitioning element immediately after the leave phase starts |
+
+This component also dispatches the following custom events, which are listened to using the Svelte `on:` directive:
+
+| Event name | Type of event `.detail` | Description |
+| ------------- | ----------------------- | ----------------------------------------------- |
+| `beforeEnter` | `null` | Dispatched before we start the enter transition |
+| `afterEnter` | `null` | Dispatched after we finish the enter transition |
+| `beforeLeave` | `null` | Dispatched before we start the leave transition |
+| `afterLeave` | `null` | Dispatched after we finish the leave transition |
+
+### TransitionChild
+
+| Prop | Default | Type | Description |
+| ----------- | ------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------- |
+| `as` | `div` | `string` | The element the `Transition` should render as |
+| `static` | `false` | `boolean` | Whether the element should ignore the internally managed open/closed state |
+| `unmount` | `true` | `boolean` | Whether the element should be unmounted, instead of just hidden, based on the open/closed state |
+| `enter` | `""` | `string` | Classes to add to the transitioning element during the entire enter phase |
+| `enterFrom` | `""` | `string` | Classes to add to the transitioning element before the enter phase starts |
+| `enterTo` | `""` | `string` | Classes to add to the transitioning element immediately after the enter phase starts |
+| `entered` | `""` | `string` | Classes to add to the transitioning element once the transition is done. These classes will persist after that until the `leave` phase |
+| `leave` | `""` | `string` | Classes to add to the transitioning element during the entire leave phase |
+| `leaveFrom` | `""` | `string` | Classes to add to the transitioning element before the leave phase starts |
+| `leaveTo` | `""` | `string` | Classes to add to the transitioning element immediately after the leave phase starts |
+
+This component also dispatches the following custom events, which are listened to using the Svelte `on:` directive:
+
+| Event name | Type of event `.detail` | Description |
+| ------------- | ----------------------- | ----------------------------------------------- |
+| `beforeEnter` | `null` | Dispatched before we start the enter transition |
+| `afterEnter` | `null` | Dispatched after we finish the enter transition |
+| `beforeLeave` | `null` | Dispatched before we start the leave transition |
+| `afterLeave` | `null` | Dispatched after we finish the leave transition |
diff --git a/src/routes/docs/version-history.svx b/src/routes/docs/version-history.svx
new file mode 100644
index 0000000..ec4306e
--- /dev/null
+++ b/src/routes/docs/version-history.svx
@@ -0,0 +1,15 @@
+
+This is the release history of Svelte Headless UI and the correspondence with versions of Headless UI.
+
+| Svelte Headless UI version | Date released | Headless UI version | Notes |
+| -------------------------- | ------------- | ------------------- | ----- |
+| 1.0.0-beta.10 | 2022-02-27 | 1.4.2 | |
+| 1.0.0-beta.9 | 2022-02-06 | 1.4.2 | |
+| 1.0.0-beta.8 | 2022-01-21 | 1.4.2 | |
+| 1.0.0-beta.7 | 2021-12-31 | 1.4.2 | |
+| 1.0.0-beta.6 | 2021-12-29 | 1.4.2 | |
+| 1.0.0-beta.5 | 2021-12-28 | 1.4.2 | |
+| 1.0.0-beta.4 | 2021-12-26 | 1.4.2 | |
+| 1.0.0-beta.3 | 2021-12-24 | 1.4.2 | Initial public release |
+
+Full release notes and changelogs can be [found on GitHub](https://github.com/tailwindlabs/headlessui/releases).
diff --git a/src/routes/index.svelte b/src/routes/index.svelte
index 4ba7b2a..d8f014f 100644
--- a/src/routes/index.svelte
+++ b/src/routes/index.svelte
@@ -1,4 +1,8 @@
-Welcome to SvelteKit
-
- Visit kit.svelte.dev to read the documentation
-
+
diff --git a/static/favicon.png b/static/favicon.png
deleted file mode 100644
index 825b9e6..0000000
Binary files a/static/favicon.png and /dev/null differ
diff --git a/static/favicon.svg b/static/favicon.svg
new file mode 100644
index 0000000..a7aaebe
--- /dev/null
+++ b/static/favicon.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/static/prism-ghcolors.css b/static/prism-ghcolors.css
new file mode 100644
index 0000000..01819a8
--- /dev/null
+++ b/static/prism-ghcolors.css
@@ -0,0 +1,122 @@
+/**
+ * GHColors theme by Avi Aryan (http://aviaryan.in)
+ * Inspired by Github syntax coloring
+ */
+
+code[class*="language-"],
+pre[class*="language-"] {
+ color: #393A34;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
+ direction: ltr;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ font-size: .9em;
+ line-height: 1.2em;
+
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+}
+
+pre > code[class*="language-"] {
+ font-size: 1em;
+}
+
+pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
+ background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
+code[class*="language-"]::selection, code[class*="language-"] ::selection {
+ background: #b3d4fc;
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+ padding: 1em;
+ margin: .5em 0;
+ overflow: auto;
+ border: 1px solid #dddddd;
+ background-color: white;
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+ padding: .2em;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ background: #f8f8f8;
+ border: 1px solid #dddddd;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: #999988;
+ font-style: italic;
+}
+
+.token.namespace {
+ opacity: .7;
+}
+
+.token.string,
+.token.attr-value {
+ color: #e3116c;
+}
+
+.token.punctuation,
+.token.operator {
+ color: #393A34; /* no highlight */
+}
+
+.token.entity,
+.token.url,
+.token.symbol,
+.token.number,
+.token.boolean,
+.token.variable,
+.token.constant,
+.token.property,
+.token.regex,
+.token.inserted {
+ color: #36acaa;
+}
+
+.token.atrule,
+.token.keyword,
+.token.attr-name,
+.language-autohotkey .token.selector {
+ color: #00a4db;
+}
+
+.token.function,
+.token.deleted,
+.language-autohotkey .token.tag {
+ color: #9a050f;
+}
+
+.token.tag,
+.token.selector,
+.language-autohotkey .token.keyword {
+ color: #00009f;
+}
+
+.token.important,
+.token.function,
+.token.bold {
+ font-weight: bold;
+}
+
+.token.italic {
+ font-style: italic;
+}
diff --git a/static/prism-one-light.css b/static/prism-one-light.css
new file mode 100644
index 0000000..44cd70f
--- /dev/null
+++ b/static/prism-one-light.css
@@ -0,0 +1,434 @@
+/**
+ * One Light theme for prism.js
+ * Based on Atom's One Light theme: https://github.com/atom/atom/tree/master/packages/one-light-syntax
+ */
+
+/**
+ * One Light colours (accurate as of commit eb064bf on 19 Feb 2021)
+ * From colors.less
+ * --mono-1: hsl(230, 8%, 24%);
+ * --mono-2: hsl(230, 6%, 44%);
+ * --mono-3: hsl(230, 4%, 64%)
+ * --hue-1: hsl(198, 99%, 37%);
+ * --hue-2: hsl(221, 87%, 60%);
+ * --hue-3: hsl(301, 63%, 40%);
+ * --hue-4: hsl(119, 34%, 47%);
+ * --hue-5: hsl(5, 74%, 59%);
+ * --hue-5-2: hsl(344, 84%, 43%);
+ * --hue-6: hsl(35, 99%, 36%);
+ * --hue-6-2: hsl(35, 99%, 40%);
+ * --syntax-fg: hsl(230, 8%, 24%);
+ * --syntax-bg: hsl(230, 1%, 98%);
+ * --syntax-gutter: hsl(230, 1%, 62%);
+ * --syntax-guide: hsla(230, 8%, 24%, 0.2);
+ * --syntax-accent: hsl(230, 100%, 66%);
+ * From syntax-variables.less
+ * --syntax-selection-color: hsl(230, 1%, 90%);
+ * --syntax-gutter-background-color-selected: hsl(230, 1%, 90%);
+ * --syntax-cursor-line: hsla(230, 8%, 24%, 0.05);
+ */
+
+code[class*="language-"],
+pre[class*="language-"] {
+ background: hsl(230, 1%, 98%);
+ color: hsl(230, 8%, 24%);
+ font-family: "Fira Code", "Fira Mono", Menlo, Consolas, "DejaVu Sans Mono",
+ monospace;
+ direction: ltr;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ line-height: 1.5;
+ -moz-tab-size: 2;
+ -o-tab-size: 2;
+ tab-size: 2;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+}
+
+/* Selection */
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] *::-moz-selection,
+pre[class*="language-"] *::-moz-selection {
+ background: hsl(230, 1%, 90%);
+ color: inherit;
+}
+
+code[class*="language-"]::selection,
+code[class*="language-"] *::selection,
+pre[class*="language-"] *::selection {
+ background: hsl(230, 1%, 90%);
+ color: inherit;
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+ padding: 1em;
+ margin: 0.5em 0;
+ overflow: auto;
+ border-radius: 0.3em;
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+ padding: 0.2em 0.3em;
+ border-radius: 0.3em;
+ white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.cdata {
+ color: hsl(230, 4%, 64%);
+}
+
+.token.doctype,
+.token.punctuation,
+.token.entity {
+ color: hsl(230, 8%, 24%);
+}
+
+.token.attr-name,
+.token.class-name,
+.token.boolean,
+.token.constant,
+.token.number,
+.token.atrule {
+ color: hsl(35, 99%, 36%);
+}
+
+.token.keyword {
+ color: hsl(301, 63%, 40%);
+}
+
+.token.property,
+.token.tag,
+.token.symbol,
+.token.deleted,
+.token.important {
+ color: hsl(5, 74%, 59%);
+}
+
+.token.selector,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted,
+.token.regex,
+.token.attr-value,
+.token.attr-value > .token.punctuation {
+ color: hsl(119, 34%, 47%);
+}
+
+.token.variable,
+.token.operator,
+.token.function {
+ color: hsl(221, 87%, 60%);
+}
+
+.token.url {
+ color: hsl(198, 99%, 37%);
+}
+
+/* HTML overrides */
+.token.attr-value > .token.punctuation.attr-equals,
+.token.special-attr > .token.attr-value > .token.value.css {
+ color: hsl(230, 8%, 24%);
+}
+
+/* CSS overrides */
+.language-css .token.selector {
+ color: hsl(5, 74%, 59%);
+}
+
+.language-css .token.property {
+ color: hsl(230, 8%, 24%);
+}
+
+.language-css .token.function,
+.language-css .token.url > .token.function {
+ color: hsl(198, 99%, 37%);
+}
+
+.language-css .token.url > .token.string.url {
+ color: hsl(119, 34%, 47%);
+}
+
+.language-css .token.important,
+.language-css .token.atrule .token.rule {
+ color: hsl(301, 63%, 40%);
+}
+
+/* JS overrides */
+.language-javascript .token.operator {
+ color: hsl(301, 63%, 40%);
+}
+
+.language-javascript
+ .token.template-string
+ > .token.interpolation
+ > .token.interpolation-punctuation.punctuation {
+ color: hsl(344, 84%, 43%);
+}
+
+/* JSON overrides */
+.language-json .token.operator {
+ color: hsl(230, 8%, 24%);
+}
+
+.language-json .token.null.keyword {
+ color: hsl(35, 99%, 36%);
+}
+
+/* MD overrides */
+.language-markdown .token.url,
+.language-markdown .token.url > .token.operator,
+.language-markdown .token.url-reference.url > .token.string {
+ color: hsl(230, 8%, 24%);
+}
+
+.language-markdown .token.url > .token.content {
+ color: hsl(221, 87%, 60%);
+}
+
+.language-markdown .token.url > .token.url,
+.language-markdown .token.url-reference.url {
+ color: hsl(198, 99%, 37%);
+}
+
+.language-markdown .token.blockquote.punctuation,
+.language-markdown .token.hr.punctuation {
+ color: hsl(230, 4%, 64%);
+ font-style: italic;
+}
+
+.language-markdown .token.code-snippet {
+ color: hsl(119, 34%, 47%);
+}
+
+.language-markdown .token.bold .token.content {
+ color: hsl(35, 99%, 36%);
+}
+
+.language-markdown .token.italic .token.content {
+ color: hsl(301, 63%, 40%);
+}
+
+.language-markdown .token.strike .token.content,
+.language-markdown .token.strike .token.punctuation,
+.language-markdown .token.list.punctuation,
+.language-markdown .token.title.important > .token.punctuation {
+ color: hsl(5, 74%, 59%);
+}
+
+/* General */
+.token.bold {
+ font-weight: bold;
+}
+
+.token.comment,
+.token.italic {
+ font-style: italic;
+}
+
+.token.entity {
+ cursor: help;
+}
+
+.token.namespace {
+ opacity: 0.8;
+}
+
+/* Plugin overrides */
+/* Selectors should have higher specificity than those in the plugins' default stylesheets */
+
+/* Show Invisibles plugin overrides */
+.token.token.tab:not(:empty):before,
+.token.token.cr:before,
+.token.token.lf:before,
+.token.token.space:before {
+ color: hsla(230, 8%, 24%, 0.2);
+}
+
+/* Toolbar plugin overrides */
+/* Space out all buttons and move them away from the right edge of the code block */
+div.code-toolbar > .toolbar.toolbar > .toolbar-item {
+ margin-right: 0.4em;
+}
+
+/* Styling the buttons */
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > button,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > a,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > span {
+ background: hsl(230, 1%, 90%);
+ color: hsl(230, 6%, 44%);
+ padding: 0.1em 0.4em;
+ border-radius: 0.3em;
+}
+
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus {
+ background: hsl(230, 1%, 78%); /* custom: darken(--syntax-bg, 20%) */
+ color: hsl(230, 8%, 24%);
+}
+
+/* Line Highlight plugin overrides */
+/* The highlighted line itself */
+.line-highlight.line-highlight {
+ background: hsla(230, 8%, 24%, 0.05);
+}
+
+/* Default line numbers in Line Highlight plugin */
+.line-highlight.line-highlight:before,
+.line-highlight.line-highlight[data-end]:after {
+ background: hsl(230, 1%, 90%);
+ color: hsl(230, 8%, 24%);
+ padding: 0.1em 0.6em;
+ border-radius: 0.3em;
+ box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2); /* same as Toolbar plugin default */
+}
+
+/* Hovering over a linkable line number (in the gutter area) */
+/* Requires Line Numbers plugin as well */
+pre[id].linkable-line-numbers.linkable-line-numbers
+ span.line-numbers-rows
+ > span:hover:before {
+ background-color: hsla(230, 8%, 24%, 0.05);
+}
+
+/* Line Numbers and Command Line plugins overrides */
+/* Line separating gutter from coding area */
+.line-numbers.line-numbers .line-numbers-rows,
+.command-line .command-line-prompt {
+ border-right-color: hsla(230, 8%, 24%, 0.2);
+}
+
+/* Stuff in the gutter */
+.line-numbers .line-numbers-rows > span:before,
+.command-line .command-line-prompt > span:before {
+ color: hsl(230, 1%, 62%);
+}
+
+/* Match Braces plugin overrides */
+/* Note: Outline colour is inherited from the braces */
+.rainbow-braces .token.token.punctuation.brace-level-1,
+.rainbow-braces .token.token.punctuation.brace-level-5,
+.rainbow-braces .token.token.punctuation.brace-level-9 {
+ color: hsl(5, 74%, 59%);
+}
+
+.rainbow-braces .token.token.punctuation.brace-level-2,
+.rainbow-braces .token.token.punctuation.brace-level-6,
+.rainbow-braces .token.token.punctuation.brace-level-10 {
+ color: hsl(119, 34%, 47%);
+}
+
+.rainbow-braces .token.token.punctuation.brace-level-3,
+.rainbow-braces .token.token.punctuation.brace-level-7,
+.rainbow-braces .token.token.punctuation.brace-level-11 {
+ color: hsl(221, 87%, 60%);
+}
+
+.rainbow-braces .token.token.punctuation.brace-level-4,
+.rainbow-braces .token.token.punctuation.brace-level-8,
+.rainbow-braces .token.token.punctuation.brace-level-12 {
+ color: hsl(301, 63%, 40%);
+}
+
+/* Diff Highlight plugin overrides */
+/* Taken from https://github.com/atom/github/blob/master/styles/variables.less */
+pre.diff-highlight > code .token.token.deleted:not(.prefix),
+pre > code.diff-highlight .token.token.deleted:not(.prefix) {
+ background-color: hsla(353, 100%, 66%, 0.15);
+}
+
+pre.diff-highlight > code .token.token.deleted:not(.prefix)::-moz-selection,
+pre.diff-highlight > code .token.token.deleted:not(.prefix) *::-moz-selection,
+pre > code.diff-highlight .token.token.deleted:not(.prefix)::-moz-selection,
+pre > code.diff-highlight .token.token.deleted:not(.prefix) *::-moz-selection {
+ background-color: hsla(353, 95%, 66%, 0.25);
+}
+
+pre.diff-highlight > code .token.token.deleted:not(.prefix)::selection,
+pre.diff-highlight > code .token.token.deleted:not(.prefix) *::selection,
+pre > code.diff-highlight .token.token.deleted:not(.prefix)::selection,
+pre > code.diff-highlight .token.token.deleted:not(.prefix) *::selection {
+ background-color: hsla(353, 95%, 66%, 0.25);
+}
+
+pre.diff-highlight > code .token.token.inserted:not(.prefix),
+pre > code.diff-highlight .token.token.inserted:not(.prefix) {
+ background-color: hsla(137, 100%, 55%, 0.15);
+}
+
+pre.diff-highlight > code .token.token.inserted:not(.prefix)::-moz-selection,
+pre.diff-highlight > code .token.token.inserted:not(.prefix) *::-moz-selection,
+pre > code.diff-highlight .token.token.inserted:not(.prefix)::-moz-selection,
+pre > code.diff-highlight .token.token.inserted:not(.prefix) *::-moz-selection {
+ background-color: hsla(135, 73%, 55%, 0.25);
+}
+
+pre.diff-highlight > code .token.token.inserted:not(.prefix)::selection,
+pre.diff-highlight > code .token.token.inserted:not(.prefix) *::selection,
+pre > code.diff-highlight .token.token.inserted:not(.prefix)::selection,
+pre > code.diff-highlight .token.token.inserted:not(.prefix) *::selection {
+ background-color: hsla(135, 73%, 55%, 0.25);
+}
+
+/* Previewers plugin overrides */
+/* Based on https://github.com/atom-community/atom-ide-datatip/blob/master/styles/atom-ide-datatips.less and https://github.com/atom/atom/blob/master/packages/one-light-ui */
+/* Border around popup */
+.prism-previewer.prism-previewer:before,
+.prism-previewer-gradient.prism-previewer-gradient div {
+ border-color: hsl(0, 0, 95%);
+}
+
+/* Angle and time should remain as circles and are hence not included */
+.prism-previewer-color.prism-previewer-color:before,
+.prism-previewer-gradient.prism-previewer-gradient div,
+.prism-previewer-easing.prism-previewer-easing:before {
+ border-radius: 0.3em;
+}
+
+/* Triangles pointing to the code */
+.prism-previewer.prism-previewer:after {
+ border-top-color: hsl(0, 0, 95%);
+}
+
+.prism-previewer-flipped.prism-previewer-flipped.after {
+ border-bottom-color: hsl(0, 0, 95%);
+}
+
+/* Background colour within the popup */
+.prism-previewer-angle.prism-previewer-angle:before,
+.prism-previewer-time.prism-previewer-time:before,
+.prism-previewer-easing.prism-previewer-easing {
+ background: hsl(0, 0%, 100%);
+}
+
+/* For angle, this is the positive area (eg. 90deg will display one quadrant in this colour) */
+/* For time, this is the alternate colour */
+.prism-previewer-angle.prism-previewer-angle circle,
+.prism-previewer-time.prism-previewer-time circle {
+ stroke: hsl(230, 8%, 24%);
+ stroke-opacity: 1;
+}
+
+/* Stroke colours of the handle, direction point, and vector itself */
+.prism-previewer-easing.prism-previewer-easing circle,
+.prism-previewer-easing.prism-previewer-easing path,
+.prism-previewer-easing.prism-previewer-easing line {
+ stroke: hsl(230, 8%, 24%);
+}
+
+/* Fill colour of the handle */
+.prism-previewer-easing.prism-previewer-easing circle {
+ fill: transparent;
+}
diff --git a/svelte.config.js b/svelte.config.js
index 78457d1..a9eb974 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -1,14 +1,19 @@
+import { mdsvex } from "mdsvex";
+import mdsvexConfig from "./mdsvex.config.js";
import adapter from "@sveltejs/adapter-auto";
import preprocess from "svelte-preprocess";
/** @type {import('@sveltejs/kit').Config} */
const config = {
+ extensions: [".svelte", ...mdsvexConfig.extensions],
+
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: [
preprocess({
postcss: true,
}),
+ mdsvex(mdsvexConfig),
],
kit: {
diff --git a/tailwind.config.cjs b/tailwind.config.cjs
index cc6fdea..031a394 100644
--- a/tailwind.config.cjs
+++ b/tailwind.config.cjs
@@ -1,5 +1,5 @@
const config = {
- content: ["./src/**/*.{html,js,svelte,ts}"],
+ content: ["./src/**/*.{html,js,svelte,ts,svx}"],
theme: {
extend: {},
@@ -9,6 +9,7 @@ const config = {
require("@tailwindcss/forms")({
strategy: "class",
}),
+ require("@tailwindcss/typography"),
],
};