mirror of
https://github.com/kremalicious/blog.git
synced 2024-06-28 16:48:00 +02:00
svg icon build system, theme switch
This commit is contained in:
parent
6d4e7f3a2a
commit
73bc1bd199
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -10,5 +10,7 @@ src/@types/Gatsby.d.ts
|
|||
|
||||
# build output
|
||||
dist/
|
||||
src/images/icons/
|
||||
|
||||
# generated types
|
||||
.astro/
|
386
package-lock.json
generated
386
package-lock.json
generated
|
@ -13,7 +13,7 @@
|
|||
"@astrojs/rss": "^3.0.0",
|
||||
"@astrojs/sitemap": "^3.0.0",
|
||||
"@rainbow-me/rainbowkit": "^1.0.9",
|
||||
"astro": "^3.0.3",
|
||||
"astro": "^3.0.7",
|
||||
"classnames": "^2.3.2",
|
||||
"date-fns": "^2.30.0",
|
||||
"dms2dec": "^1.1.0",
|
||||
|
@ -28,7 +28,6 @@
|
|||
"react": "^18.2.0",
|
||||
"react-clipboard.js": "^2.0.16",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"slugify": "^1.6.6",
|
||||
"use-debounce": "^9.0.4",
|
||||
"viem": "^1.9.0",
|
||||
|
@ -64,12 +63,13 @@
|
|||
"stylelint-config-css-modules": "^4.3.0",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-prettier": "^4.0.2",
|
||||
"svgo": "^3.0.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2",
|
||||
"typescript-plugin-css-modules": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": "18"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
|
@ -232,9 +232,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@astrojs/telemetry": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.0.0.tgz",
|
||||
"integrity": "sha512-RhFlEXTiT0gbWX1osMuPS9IWm1SwhQmCZVAdAixrPyZ0xiLlHfw3Nkw3z6IYuzX3hqbx24G4XmkT/akBMBqxPg==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.0.1.tgz",
|
||||
"integrity": "sha512-7zJMuikRDQ0LLLivteu0+y4pqdgznrChFiRrY3qmKlOEkLWD1T3u1a5M970lvpErP7Vgh4P298JBPjv8LTj+sw==",
|
||||
"dependencies": {
|
||||
"ci-info": "^3.8.0",
|
||||
"debug": "^4.3.4",
|
||||
|
@ -4505,15 +4505,6 @@
|
|||
"@svgr/core": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@svgr/plugin-svgo/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@svgr/plugin-svgo/node_modules/cosmiconfig": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
|
||||
|
@ -4532,82 +4523,6 @@
|
|||
"url": "https://github.com/sponsors/d-fischer"
|
||||
}
|
||||
},
|
||||
"node_modules/@svgr/plugin-svgo/node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@svgr/plugin-svgo/node_modules/csso": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
||||
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"css-tree": "~2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@svgr/plugin-svgo/node_modules/csso/node_modules/css-tree": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
||||
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.28",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@svgr/plugin-svgo/node_modules/csso/node_modules/mdn-data": {
|
||||
"version": "2.0.28",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@svgr/plugin-svgo/node_modules/mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@svgr/plugin-svgo/node_modules/svgo": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz",
|
||||
"integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@trysound/sax": "0.2.0",
|
||||
"commander": "^7.2.0",
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^2.2.1",
|
||||
"csso": "^5.0.5",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"svgo": "bin/svgo"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/svgo"
|
||||
}
|
||||
},
|
||||
"node_modules/@svgr/webpack": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz",
|
||||
|
@ -6689,14 +6604,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-3.0.3.tgz",
|
||||
"integrity": "sha512-bugdGn9wIniVFbfyAHYtF9bc9pZpPaEs3gJAnK/XWROxCBAI2UQjR6lQuWM20iCc3snqu7GDgoW2MdzO7WFZZw==",
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-3.0.7.tgz",
|
||||
"integrity": "sha512-slUnDBXfxMzq5abE4svcKbaeYC/tHZsJYOrzwDNU9lLye3/4cqYP7OuHMTXiRlx7LSpHQlUhwbMe2HqCv2GKag==",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.0.1",
|
||||
"@astrojs/internal-helpers": "0.2.0",
|
||||
"@astrojs/markdown-remark": "3.0.0",
|
||||
"@astrojs/telemetry": "3.0.0",
|
||||
"@astrojs/telemetry": "3.0.1",
|
||||
"@babel/core": "^7.22.10",
|
||||
"@babel/generator": "^7.22.10",
|
||||
"@babel/parser": "^7.22.10",
|
||||
|
@ -8375,6 +8290,19 @@
|
|||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-what": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
|
||||
|
@ -8403,6 +8331,39 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/csso": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
||||
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"css-tree": "~2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/csso/node_modules/css-tree": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
||||
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.28",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/csso/node_modules/mdn-data": {
|
||||
"version": "2.0.28",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cssom": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
|
||||
|
@ -14978,6 +14939,12 @@
|
|||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
|
||||
|
@ -17308,17 +17275,6 @@
|
|||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-feather": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/react-feather/-/react-feather-2.0.10.tgz",
|
||||
"integrity": "sha512-BLhukwJ+Z92Nmdcs+EMw6dy1Z/VLiJTzEQACDUEnWMClhYnFykJCGWQx+NmwP/qQHGX/5CzQ+TGi8ofg2+HzVQ==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
@ -19508,25 +19464,6 @@
|
|||
"url": "https://github.com/sponsors/d-fischer"
|
||||
}
|
||||
},
|
||||
"node_modules/stylelint/node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stylelint/node_modules/mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/stylelint/node_modules/signal-exit": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz",
|
||||
|
@ -19656,6 +19593,39 @@
|
|||
"integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz",
|
||||
"integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@trysound/sax": "0.2.0",
|
||||
"commander": "^7.2.0",
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^2.2.1",
|
||||
"csso": "^5.0.5",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"svgo": "bin/svgo"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/svgo"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||
|
@ -21970,9 +21940,9 @@
|
|||
}
|
||||
},
|
||||
"@astrojs/telemetry": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.0.0.tgz",
|
||||
"integrity": "sha512-RhFlEXTiT0gbWX1osMuPS9IWm1SwhQmCZVAdAixrPyZ0xiLlHfw3Nkw3z6IYuzX3hqbx24G4XmkT/akBMBqxPg==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.0.1.tgz",
|
||||
"integrity": "sha512-7zJMuikRDQ0LLLivteu0+y4pqdgznrChFiRrY3qmKlOEkLWD1T3u1a5M970lvpErP7Vgh4P298JBPjv8LTj+sw==",
|
||||
"requires": {
|
||||
"ci-info": "^3.8.0",
|
||||
"debug": "^4.3.4",
|
||||
|
@ -24922,12 +24892,6 @@
|
|||
"svgo": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
|
||||
|
@ -24939,63 +24903,6 @@
|
|||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"csso": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
||||
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"css-tree": "~2.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"css-tree": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
||||
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mdn-data": "2.0.28",
|
||||
"source-map-js": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"mdn-data": {
|
||||
"version": "2.0.28",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true
|
||||
},
|
||||
"svgo": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz",
|
||||
"integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@trysound/sax": "0.2.0",
|
||||
"commander": "^7.2.0",
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^2.2.1",
|
||||
"csso": "^5.0.5",
|
||||
"picocolors": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -26660,14 +26567,14 @@
|
|||
"dev": true
|
||||
},
|
||||
"astro": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-3.0.3.tgz",
|
||||
"integrity": "sha512-bugdGn9wIniVFbfyAHYtF9bc9pZpPaEs3gJAnK/XWROxCBAI2UQjR6lQuWM20iCc3snqu7GDgoW2MdzO7WFZZw==",
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-3.0.7.tgz",
|
||||
"integrity": "sha512-slUnDBXfxMzq5abE4svcKbaeYC/tHZsJYOrzwDNU9lLye3/4cqYP7OuHMTXiRlx7LSpHQlUhwbMe2HqCv2GKag==",
|
||||
"requires": {
|
||||
"@astrojs/compiler": "^2.0.1",
|
||||
"@astrojs/internal-helpers": "0.2.0",
|
||||
"@astrojs/markdown-remark": "3.0.0",
|
||||
"@astrojs/telemetry": "3.0.0",
|
||||
"@astrojs/telemetry": "3.0.1",
|
||||
"@babel/core": "^7.22.10",
|
||||
"@babel/generator": "^7.22.10",
|
||||
"@babel/parser": "^7.22.10",
|
||||
|
@ -27835,6 +27742,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"css-what": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
|
||||
|
@ -27851,6 +27768,33 @@
|
|||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
|
||||
},
|
||||
"csso": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
||||
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"css-tree": "~2.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"css-tree": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
||||
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mdn-data": "2.0.28",
|
||||
"source-map-js": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"mdn-data": {
|
||||
"version": "2.0.28",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"cssom": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
|
||||
|
@ -32675,6 +32619,12 @@
|
|||
"@types/mdast": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true
|
||||
},
|
||||
"mdurl": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
|
||||
|
@ -34241,14 +34191,6 @@
|
|||
"scheduler": "^0.23.0"
|
||||
}
|
||||
},
|
||||
"react-feather": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/react-feather/-/react-feather-2.0.10.tgz",
|
||||
"integrity": "sha512-BLhukwJ+Z92Nmdcs+EMw6dy1Z/VLiJTzEQACDUEnWMClhYnFykJCGWQx+NmwP/qQHGX/5CzQ+TGi8ofg2+HzVQ==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
@ -35814,22 +35756,6 @@
|
|||
"path-type": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz",
|
||||
|
@ -35974,6 +35900,28 @@
|
|||
"integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
|
||||
"dev": true
|
||||
},
|
||||
"svgo": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz",
|
||||
"integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@trysound/sax": "0.2.0",
|
||||
"commander": "^7.2.0",
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^2.2.1",
|
||||
"csso": "^5.0.5",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||
|
|
15
package.json
15
package.json
|
@ -5,10 +5,10 @@
|
|||
"description": "Blog of Designer & Developer Matthias Kretschmann",
|
||||
"homepage": "https://kremalicious.com",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "astro dev --config .config/astro.config.mjs",
|
||||
"start": "astro dev --config .config/astro.config.mjs",
|
||||
"build": "astro build --config .config/astro.config.mjs",
|
||||
"start": "npm run create:icons && astro dev --config .config/astro.config.mjs",
|
||||
"build": "npm run create:icons && astro build --config .config/astro.config.mjs",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"test": "astro check && tsc --noEmit && npm run lint && npm run type-check && npm run jest",
|
||||
|
@ -20,14 +20,15 @@
|
|||
"format": "prettier --ignore-path .gitignore --write '**/*.{js,jsx,ts,tsx,md,json,css}'",
|
||||
"type-check": "tsc --noEmit",
|
||||
"deploy:s3": "./scripts/deploy-s3.sh",
|
||||
"new": "ts-node scripts/new.ts"
|
||||
"new": "ts-node --esm scripts/new.ts",
|
||||
"create:icons": "ts-node --esm scripts/create-icons/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/react": "^3.0.0",
|
||||
"@astrojs/rss": "^3.0.0",
|
||||
"@astrojs/sitemap": "^3.0.0",
|
||||
"@rainbow-me/rainbowkit": "^1.0.9",
|
||||
"astro": "^3.0.3",
|
||||
"astro": "^3.0.7",
|
||||
"classnames": "^2.3.2",
|
||||
"date-fns": "^2.30.0",
|
||||
"dms2dec": "^1.1.0",
|
||||
|
@ -42,7 +43,6 @@
|
|||
"react": "^18.2.0",
|
||||
"react-clipboard.js": "^2.0.16",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"slugify": "^1.6.6",
|
||||
"use-debounce": "^9.0.4",
|
||||
"viem": "^1.9.0",
|
||||
|
@ -78,12 +78,13 @@
|
|||
"stylelint-config-css-modules": "^4.3.0",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-prettier": "^4.0.2",
|
||||
"svgo": "^3.0.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2",
|
||||
"typescript-plugin-css-modules": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": "18"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
320
scripts/create-icons/Props.ts
Normal file
320
scripts/create-icons/Props.ts
Normal file
|
@ -0,0 +1,320 @@
|
|||
// All the WAI-ARIA 1.1 attributes from https://www.w3.org/TR/wai-aria-1.1/
|
||||
export interface AriaAttributes {
|
||||
/** Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application. */
|
||||
'aria-activedescendant'?: string
|
||||
/** Indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute. */
|
||||
'aria-atomic'?: 'true' | 'false' | boolean
|
||||
/**
|
||||
* Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be
|
||||
* presented if they are made.
|
||||
*/
|
||||
'aria-autocomplete'?: 'none' | 'inline' | 'list' | 'both'
|
||||
/** Indicates an element is being modified and that assistive technologies MAY want to wait until the modifications are complete before exposing them to the user. */
|
||||
'aria-busy'?: 'true' | 'false' | boolean
|
||||
/**
|
||||
* Indicates the current 'checked' state of checkboxes, radio buttons, and other widgets.
|
||||
* @see aria-pressed @see aria-selected.
|
||||
*/
|
||||
'aria-checked'?: 'false' | 'mixed' | 'true' | boolean
|
||||
/**
|
||||
* Defines the total number of columns in a table, grid, or treegrid.
|
||||
* @see aria-colindex.
|
||||
*/
|
||||
'aria-colcount'?: number
|
||||
/**
|
||||
* Defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.
|
||||
* @see aria-colcount @see aria-colspan.
|
||||
*/
|
||||
'aria-colindex'?: number
|
||||
/**
|
||||
* Defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.
|
||||
* @see aria-colindex @see aria-rowspan.
|
||||
*/
|
||||
'aria-colspan'?: number
|
||||
/**
|
||||
* Identifies the element (or elements) whose contents or presence are controlled by the current element.
|
||||
* @see aria-owns.
|
||||
*/
|
||||
'aria-controls'?: string
|
||||
/** Indicates the element that represents the current item within a container or set of related elements. */
|
||||
'aria-current'?:
|
||||
| 'false'
|
||||
| 'true'
|
||||
| 'page'
|
||||
| 'step'
|
||||
| 'location'
|
||||
| 'date'
|
||||
| 'time'
|
||||
| boolean
|
||||
/**
|
||||
* Identifies the element (or elements) that describes the object.
|
||||
* @see aria-labelledby
|
||||
*/
|
||||
'aria-describedby'?: string
|
||||
/**
|
||||
* Identifies the element that provides a detailed, extended description for the object.
|
||||
* @see aria-describedby.
|
||||
*/
|
||||
'aria-details'?: string
|
||||
/**
|
||||
* Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
|
||||
* @see aria-hidden @see aria-readonly.
|
||||
*/
|
||||
'aria-disabled'?: 'true' | 'false' | boolean
|
||||
/**
|
||||
* Identifies the element that provides an error message for the object.
|
||||
* @see aria-invalid @see aria-describedby.
|
||||
*/
|
||||
'aria-errormessage'?: string
|
||||
/** Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed. */
|
||||
'aria-expanded'?: 'true' | 'false' | boolean
|
||||
/**
|
||||
* Identifies the next element (or elements) in an alternate reading order of content which, at the user's discretion,
|
||||
* allows assistive technology to override the general default of reading in document source order.
|
||||
*/
|
||||
'aria-flowto'?: string
|
||||
/** Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element. */
|
||||
'aria-haspopup'?:
|
||||
| 'false'
|
||||
| 'true'
|
||||
| 'menu'
|
||||
| 'listbox'
|
||||
| 'tree'
|
||||
| 'grid'
|
||||
| 'dialog'
|
||||
| boolean
|
||||
/**
|
||||
* Indicates whether the element is exposed to an accessibility API.
|
||||
* @see aria-disabled.
|
||||
*/
|
||||
'aria-hidden'?: 'true' | 'false' | boolean
|
||||
/**
|
||||
* Indicates the entered value does not conform to the format expected by the application.
|
||||
* @see aria-errormessage.
|
||||
*/
|
||||
'aria-invalid'?: 'false' | 'true' | 'grammar' | 'spelling' | boolean
|
||||
/** Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element. */
|
||||
'aria-keyshortcuts'?: string
|
||||
/**
|
||||
* Defines a string value that labels the current element.
|
||||
* @see aria-labelledby.
|
||||
*/
|
||||
'aria-label'?: string
|
||||
/**
|
||||
* Identifies the element (or elements) that labels the current element.
|
||||
* @see aria-describedby.
|
||||
*/
|
||||
'aria-labelledby'?: string
|
||||
/** Defines the hierarchical level of an element within a structure. */
|
||||
'aria-level'?: number
|
||||
/** Indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region. */
|
||||
'aria-live'?: 'off' | 'assertive' | 'polite'
|
||||
/** Indicates whether an element is modal when displayed. */
|
||||
'aria-modal'?: 'true' | 'false' | boolean
|
||||
/** Indicates whether a text box accepts multiple lines of input or only a single line. */
|
||||
'aria-multiline'?: 'true' | 'false' | boolean
|
||||
/** Indicates that the user may select more than one item from the current selectable descendants. */
|
||||
'aria-multiselectable'?: 'true' | 'false' | boolean
|
||||
/** Indicates whether the element's orientation is horizontal, vertical, or unknown/ambiguous. */
|
||||
'aria-orientation'?: 'horizontal' | 'vertical'
|
||||
/**
|
||||
* Identifies an element (or elements) in order to define a visual, functional, or contextual parent/child relationship
|
||||
* between DOM elements where the DOM hierarchy cannot be used to represent the relationship.
|
||||
* @see aria-controls.
|
||||
*/
|
||||
'aria-owns'?: string
|
||||
/**
|
||||
* Defines a short hint (a word or short phrase) intended to aid the user with data entry when the control has no value.
|
||||
* A hint could be a sample value or a brief description of the expected format.
|
||||
*/
|
||||
'aria-placeholder'?: string
|
||||
/**
|
||||
* Defines an element's number or position in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.
|
||||
* @see aria-setsize.
|
||||
*/
|
||||
'aria-posinset'?: number
|
||||
/**
|
||||
* Indicates the current 'pressed' state of toggle buttons.
|
||||
* @see aria-checked @see aria-selected.
|
||||
*/
|
||||
'aria-pressed'?: 'false' | 'mixed' | 'true' | boolean
|
||||
/**
|
||||
* Indicates that the element is not editable, but is otherwise operable.
|
||||
* @see aria-disabled.
|
||||
*/
|
||||
'aria-readonly'?: 'true' | 'false' | boolean
|
||||
/**
|
||||
* Indicates what notifications the user agent will trigger when the accessibility tree within a live region is modified.
|
||||
* @see aria-atomic.
|
||||
*/
|
||||
'aria-relevant'?:
|
||||
| 'additions'
|
||||
| 'additions removals'
|
||||
| 'additions text'
|
||||
| 'all'
|
||||
| 'removals'
|
||||
| 'removals additions'
|
||||
| 'removals text'
|
||||
| 'text'
|
||||
| 'text additions'
|
||||
| 'text removals'
|
||||
/** Indicates that user input is required on the element before a form may be submitted. */
|
||||
'aria-required'?: 'true' | 'false' | boolean
|
||||
/** Defines a human-readable, author-localized description for the role of an element. */
|
||||
'aria-roledescription'?: string
|
||||
/**
|
||||
* Defines the total number of rows in a table, grid, or treegrid.
|
||||
* @see aria-rowindex.
|
||||
*/
|
||||
'aria-rowcount'?: number
|
||||
/**
|
||||
* Defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.
|
||||
* @see aria-rowcount @see aria-rowspan.
|
||||
*/
|
||||
'aria-rowindex'?: number
|
||||
/**
|
||||
* Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.
|
||||
* @see aria-rowindex @see aria-colspan.
|
||||
*/
|
||||
'aria-rowspan'?: number
|
||||
/**
|
||||
* Indicates the current 'selected' state of various widgets.
|
||||
* @see aria-checked @see aria-pressed.
|
||||
*/
|
||||
'aria-selected'?: 'true' | 'false' | boolean
|
||||
/**
|
||||
* Defines the number of items in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.
|
||||
* @see aria-posinset.
|
||||
*/
|
||||
'aria-setsize'?: number
|
||||
/** Indicates if items in a table or grid are sorted in ascending or descending order. */
|
||||
'aria-sort'?: 'none' | 'ascending' | 'descending' | 'other'
|
||||
/** Defines the maximum allowed value for a range widget. */
|
||||
'aria-valuemax'?: number
|
||||
/** Defines the minimum allowed value for a range widget. */
|
||||
'aria-valuemin'?: number
|
||||
/**
|
||||
* Defines the current value for a range widget.
|
||||
* @see aria-valuetext.
|
||||
*/
|
||||
'aria-valuenow'?: number
|
||||
/** Defines the human readable text alternative of aria-valuenow for a range widget. */
|
||||
'aria-valuetext'?: string
|
||||
}
|
||||
|
||||
// All the WAI-ARIA 1.1 role attribute values from https://www.w3.org/TR/wai-aria-1.1/#role_definitions
|
||||
export type AriaRole =
|
||||
| 'alert'
|
||||
| 'alertdialog'
|
||||
| 'application'
|
||||
| 'article'
|
||||
| 'banner'
|
||||
| 'button'
|
||||
| 'cell'
|
||||
| 'checkbox'
|
||||
| 'columnheader'
|
||||
| 'combobox'
|
||||
| 'complementary'
|
||||
| 'contentinfo'
|
||||
| 'definition'
|
||||
| 'dialog'
|
||||
| 'directory'
|
||||
| 'document'
|
||||
| 'feed'
|
||||
| 'figure'
|
||||
| 'form'
|
||||
| 'grid'
|
||||
| 'gridcell'
|
||||
| 'group'
|
||||
| 'heading'
|
||||
| 'img'
|
||||
| 'link'
|
||||
| 'list'
|
||||
| 'listbox'
|
||||
| 'listitem'
|
||||
| 'log'
|
||||
| 'main'
|
||||
| 'marquee'
|
||||
| 'math'
|
||||
| 'menu'
|
||||
| 'menubar'
|
||||
| 'menuitem'
|
||||
| 'menuitemcheckbox'
|
||||
| 'menuitemradio'
|
||||
| 'navigation'
|
||||
| 'none'
|
||||
| 'note'
|
||||
| 'option'
|
||||
| 'presentation'
|
||||
| 'progressbar'
|
||||
| 'radio'
|
||||
| 'radiogroup'
|
||||
| 'region'
|
||||
| 'row'
|
||||
| 'rowgroup'
|
||||
| 'rowheader'
|
||||
| 'scrollbar'
|
||||
| 'search'
|
||||
| 'searchbox'
|
||||
| 'separator'
|
||||
| 'slider'
|
||||
| 'spinbutton'
|
||||
| 'status'
|
||||
| 'switch'
|
||||
| 'tab'
|
||||
| 'table'
|
||||
| 'tablist'
|
||||
| 'tabpanel'
|
||||
| 'term'
|
||||
| 'textbox'
|
||||
| 'timer'
|
||||
| 'toolbar'
|
||||
| 'tooltip'
|
||||
| 'tree'
|
||||
| 'treegrid'
|
||||
| 'treeitem'
|
||||
| (string & object)
|
||||
|
||||
export interface HTMLAttributes extends AriaAttributes {
|
||||
accesskey?: string
|
||||
autocapitalize?: string
|
||||
autofocus?: string
|
||||
class?: string
|
||||
contenteditable?: 'true' | 'false' | 'inherit' | boolean
|
||||
contextMenu?: string
|
||||
dir?: 'ltr' | 'rtl' | 'auto'
|
||||
draggable?: 'true' | 'false' | boolean
|
||||
enterkeyhint?: string
|
||||
exportparts?: string
|
||||
hidden?: string
|
||||
id?: string
|
||||
inputmode?: string
|
||||
is?: string
|
||||
lang?: string
|
||||
placeholder?: string
|
||||
role?: AriaRole
|
||||
slot?: string
|
||||
spellcheck?: 'true' | 'false' | boolean
|
||||
style?: string
|
||||
tabindex?: number
|
||||
title?: string
|
||||
translate?: 'yes' | 'no'
|
||||
}
|
||||
|
||||
export interface Props extends HTMLAttributes {
|
||||
fill?: string
|
||||
'fill-opacity'?: number | string
|
||||
'fill-rule'?: 'nonzero' | 'evenodd' | 'inherit'
|
||||
height?: number | string
|
||||
size?: number | string
|
||||
stroke?: string
|
||||
'stroke-dasharray'?: string | number
|
||||
'stroke-dashoffset'?: string | number
|
||||
'stroke-linecap'?: 'butt' | 'round' | 'square' | 'inherit'
|
||||
'stroke-linejoin'?: 'miter' | 'round' | 'bevel' | 'inherit'
|
||||
'stroke-miterlimit'?: number | string
|
||||
'stroke-opacity'?: number | string
|
||||
'stroke-width'?: number | string
|
||||
viewBox?: string
|
||||
width?: number | string
|
||||
}
|
89
scripts/create-icons/index.ts
Normal file
89
scripts/create-icons/index.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// Generate Astro components from SVG files.
|
||||
// adapted from https://github.com/astro-community/icons
|
||||
//
|
||||
import fs from 'node:fs/promises'
|
||||
import ps from 'node:path/posix'
|
||||
import { toAstroComponent, toInnerSvg } from './svg.ts'
|
||||
|
||||
// Current directory.
|
||||
const currentDir = ps.resolve('.')
|
||||
|
||||
// // Source directories
|
||||
const srcDirs = [
|
||||
ps.resolve(currentDir, 'node_modules/feather-icons/dist/icons'),
|
||||
ps.resolve(currentDir, 'src/images')
|
||||
]
|
||||
|
||||
// Distribution directory.
|
||||
const distDir = ps.resolve(currentDir, 'src/images/icons')
|
||||
|
||||
// Data related to each icon exported by this package.
|
||||
const icons = []
|
||||
|
||||
// clean the distribution directory
|
||||
await fs.rm(distDir, { force: true, recursive: true })
|
||||
await fs.mkdir(distDir, { recursive: true })
|
||||
|
||||
// copy the attribute typings file
|
||||
await fs.copyFile(
|
||||
ps.resolve(currentDir, 'scripts/create-icons/Props.ts'),
|
||||
ps.resolve(distDir, 'Props.ts')
|
||||
)
|
||||
|
||||
// convert the SVG files into Astro components
|
||||
let contentOfIndexJS = ''
|
||||
|
||||
for (const src of srcDirs) {
|
||||
for (let filepath of await fs.readdir(src, { encoding: 'utf8' })) {
|
||||
// ignore non-svg files
|
||||
if (!filepath.endsWith('.svg')) continue
|
||||
|
||||
// Base name of the SVG.
|
||||
const name = filepath.replace(/\.svg$/, '')
|
||||
|
||||
// get filepath as a full path
|
||||
filepath = ps.resolve(src, filepath)
|
||||
|
||||
// Inner contents of the SVG file.
|
||||
const innerSVG = toInnerSvg(await fs.readFile(filepath, 'utf8'))
|
||||
|
||||
// Formatted title.
|
||||
const title = name
|
||||
.replace(
|
||||
// uppercase alphabetic characters after the start or a dash
|
||||
/(?<=^|-)([a-z])/g,
|
||||
(_0, $1) => $1.toUpperCase()
|
||||
)
|
||||
.replace(
|
||||
// replace non-alphanumeric characters with space
|
||||
/[^A-Za-z0-9]+/g,
|
||||
' '
|
||||
)
|
||||
.replace(
|
||||
// respect 'GitHub' brand casing
|
||||
'Github Logo',
|
||||
'GitHub Logo'
|
||||
)
|
||||
|
||||
// Base name, which is the formatted title without spaces (PascalCase)
|
||||
const baseName = title.replace(/ /g, '')
|
||||
|
||||
// write the astro component to a file
|
||||
await fs.writeFile(
|
||||
ps.resolve(distDir, `${baseName}.astro`),
|
||||
toAstroComponent(innerSVG, title),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
// add the astro component export to the main entry `index.ts` file
|
||||
contentOfIndexJS += `\nexport { default as ${baseName} } from './${baseName}.astro'`
|
||||
|
||||
icons.push({ name, baseName, title })
|
||||
}
|
||||
}
|
||||
|
||||
// write the main entry `index.ts` file
|
||||
await fs.writeFile(ps.resolve(distDir, 'index.ts'), contentOfIndexJS, 'utf8')
|
||||
|
||||
console.log(`✔️ Generated ${icons.length} icons into @images/icons.`)
|
85
scripts/create-icons/svg.ts
Normal file
85
scripts/create-icons/svg.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
import { optimize as optimizeSVGNative } from 'svgo'
|
||||
|
||||
export const toAstroComponent = (innerSVG: string, title: string) => `---
|
||||
export { Props } from './Props.ts';
|
||||
|
||||
let {
|
||||
size = '24px',
|
||||
title,
|
||||
width = size,
|
||||
height = size,
|
||||
...props
|
||||
} = {
|
||||
'fill': 'none',
|
||||
'title': '${title}',
|
||||
'viewBox': '0 0 24 24',
|
||||
...Astro.props
|
||||
}
|
||||
|
||||
const toAttributeSize = (size: number) => String(size).replace(/(?<=[0-9])x$/, 'em')
|
||||
|
||||
size = toAttributeSize(size)
|
||||
width = toAttributeSize(width)
|
||||
height = toAttributeSize(height)
|
||||
---
|
||||
<style is:global>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
stroke: currentcolor;
|
||||
stroke-width: var(--stroke-width);
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
fill: none;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
</style>
|
||||
<svg {width} {height} {...props} class="icon">{title ? (<title>{title}</title>) : ''}${innerSVG}</svg>`
|
||||
|
||||
export const toInnerSvg = (input: string) =>
|
||||
optimizeSVGNative(input, {
|
||||
plugins: [
|
||||
'removeDoctype',
|
||||
'removeXMLProcInst',
|
||||
'removeComments',
|
||||
'removeMetadata',
|
||||
'removeXMLNS',
|
||||
'removeEditorsNSData',
|
||||
'cleanupAttrs',
|
||||
'minifyStyles',
|
||||
'convertStyleToAttrs',
|
||||
'cleanupIds',
|
||||
'removeRasterImages',
|
||||
'removeUselessDefs',
|
||||
'cleanupNumericValues',
|
||||
'cleanupListOfValues',
|
||||
'convertColors',
|
||||
'removeUnknownsAndDefaults',
|
||||
'removeNonInheritableGroupAttrs',
|
||||
'removeUselessStrokeAndFill',
|
||||
'removeViewBox',
|
||||
'cleanupEnableBackground',
|
||||
'removeHiddenElems',
|
||||
'removeEmptyText',
|
||||
'convertShapeToPath',
|
||||
'moveElemsAttrsToGroup',
|
||||
'moveGroupAttrsToElems',
|
||||
'collapseGroups',
|
||||
'convertPathData',
|
||||
'convertTransform',
|
||||
'removeEmptyAttrs',
|
||||
'removeEmptyContainers',
|
||||
'mergePaths',
|
||||
'removeUnusedNS',
|
||||
'sortAttrs',
|
||||
'removeTitle',
|
||||
'removeDesc',
|
||||
'removeDimensions',
|
||||
'removeStyleElement',
|
||||
'removeScriptElement'
|
||||
]
|
||||
})
|
||||
.data.replace(/^<svg[^>]*>|<\/svg>$/g, '')
|
||||
.replace(/ fill="currentColor"/g, '')
|
||||
.replace(/ (clip|fill)-rule="evenodd"/g, '')
|
||||
.replace(/\/>/g, ' />')
|
|
@ -1,5 +1,5 @@
|
|||
import fastExif from 'fast-exif'
|
||||
import fs from 'fs-extra'
|
||||
import fs from 'fs'
|
||||
import iptc from 'node-iptc'
|
||||
import ora from 'ora'
|
||||
import path from 'path'
|
||||
|
|
30
src/components/Footer/Networks.astro
Normal file
30
src/components/Footer/Networks.astro
Normal file
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
import styles from './Networks.module.css'
|
||||
import { Twitter, Rss, Mastodon, Github, Jsonfeed } from '@images/icons'
|
||||
|
||||
type Props = {
|
||||
links: string[]
|
||||
}
|
||||
|
||||
const { links } = Astro.props
|
||||
---
|
||||
|
||||
<p>
|
||||
{
|
||||
links.map((link: string) => (
|
||||
<a class={styles.link} href={link} title={link} rel="me">
|
||||
{link.includes('mas.to') ? (
|
||||
<Mastodon />
|
||||
) : link.includes('twitter') ? (
|
||||
<Twitter />
|
||||
) : link.includes('github') ? (
|
||||
<Github />
|
||||
) : link.includes('feed.xml') ? (
|
||||
<Rss />
|
||||
) : link.includes('feed.json') ? (
|
||||
<Jsonfeed />
|
||||
) : null}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</p>
|
|
@ -1,39 +0,0 @@
|
|||
import type { ReactElement } from 'react'
|
||||
import Icon from '../core/Icon'
|
||||
import styles from './Networks.module.css'
|
||||
|
||||
function NetworkIcon({ link }: { link: string }) {
|
||||
let IconComp
|
||||
|
||||
if (link.includes('mas.to')) {
|
||||
IconComp = <Icon name="Mastodon" />
|
||||
} else if (link.includes('twitter')) {
|
||||
IconComp = <Icon name="Twitter" />
|
||||
} else if (link.includes('github')) {
|
||||
IconComp = <Icon name="GitHub" />
|
||||
} else if (link.includes('feed.xml')) {
|
||||
IconComp = <Icon name="Rss" />
|
||||
} else if (link.includes('feed.json')) {
|
||||
IconComp = <Icon name="Jsonfeed" />
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
return IconComp
|
||||
}
|
||||
|
||||
export default function IconLinks({
|
||||
links
|
||||
}: {
|
||||
links: string[]
|
||||
}): ReactElement {
|
||||
return (
|
||||
<p>
|
||||
{links.map((link: string) => (
|
||||
<a key={link} className={styles.link} href={link} title={link} rel="me">
|
||||
<NetworkIcon link={link} />
|
||||
</a>
|
||||
))}
|
||||
</p>
|
||||
)
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
import { Image } from 'astro:assets'
|
||||
import Networks from './Networks'
|
||||
import avatar from '../../images/avatar.jpg'
|
||||
import config from '../../../.config/blog.config.mjs'
|
||||
import Networks from './Networks.astro'
|
||||
import avatar from '@images/avatar.jpg'
|
||||
import config from '@config/blog.config.mjs'
|
||||
|
||||
const { author, rss, jsonfeed } = config
|
||||
const { mastodon, twitter, github, name, uri } = author
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
import Icon from '../core/Icon'
|
||||
import { Github, Bitcoin } from '@images/icons'
|
||||
import Vcard from './Vcard.astro'
|
||||
import styles from './index.module.css'
|
||||
import config from '@config/blog.config.mjs'
|
||||
|
@ -18,11 +18,11 @@ const { name, uri, github } = config.author
|
|||
{name}
|
||||
</a>
|
||||
<a href={`${github}/blog`}>
|
||||
<Icon name="GitHub" />
|
||||
<Github />
|
||||
View source
|
||||
</a>
|
||||
<a href="/thanks" class={styles.btc}>
|
||||
<Icon name="Bitcoin" />
|
||||
<Bitcoin />
|
||||
Say Thanks
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { ReactElement } from 'react'
|
||||
import Icon from '../../core/Icon'
|
||||
import styles from './SearchButton.module.css'
|
||||
import { Search } from '@images/icons'
|
||||
|
||||
const SearchButton = ({ onClick }: { onClick: () => void }): ReactElement => (
|
||||
<button
|
||||
|
@ -9,7 +9,7 @@ const SearchButton = ({ onClick }: { onClick: () => void }): ReactElement => (
|
|||
className={styles.searchButton}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon name="Search" />
|
||||
<Search />
|
||||
</button>
|
||||
)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { ChangeEvent, ReactElement } from 'react'
|
||||
import Icon from '../../core/Icon'
|
||||
import Input from '../../core/Input'
|
||||
import styles from './SearchInput.module.css'
|
||||
import { X } from '@images/icons'
|
||||
|
||||
export default function SearchInput({
|
||||
value,
|
||||
|
@ -27,7 +27,7 @@ export default function SearchInput({
|
|||
onClick={onToggle}
|
||||
title="Close search"
|
||||
>
|
||||
<Icon name="X" />
|
||||
<X />
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import React from 'react'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import ThemeSwitch from './ThemeSwitch'
|
||||
|
||||
describe('ThemeSwitch', () => {
|
||||
it('renders correctly', async () => {
|
||||
render(<ThemeSwitch />)
|
||||
const element = await screen.findByTitle('Toggle Dark Mode')
|
||||
expect(element).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('checkbox can be changed', () => {
|
||||
const { container } = render(<ThemeSwitch />)
|
||||
|
||||
const toggle = container.querySelector('input')
|
||||
const label = container.querySelector('label')
|
||||
fireEvent.click(label)
|
||||
fireEvent.change(toggle, { target: { checked: true } })
|
||||
})
|
||||
})
|
|
@ -1,33 +0,0 @@
|
|||
import type { ReactElement } from 'react'
|
||||
import useDarkMode from '../../hooks/useDarkMode'
|
||||
import Icon from '../core/Icon'
|
||||
import styles from './ThemeSwitch.module.css'
|
||||
|
||||
export default function ThemeSwitch(): ReactElement {
|
||||
const { isDarkMode, setIsDarkMode } = useDarkMode()
|
||||
|
||||
return (
|
||||
<div className={styles.themeSwitch} title="Toggle Dark Mode">
|
||||
<label
|
||||
htmlFor="toggle"
|
||||
className={styles.checkbox}
|
||||
onClick={() => setIsDarkMode(!isDarkMode)}
|
||||
onKeyPress={() => setIsDarkMode(!isDarkMode)}
|
||||
role="presentation"
|
||||
>
|
||||
<span className={styles.label}>Toggle Dark Mode</span>
|
||||
<input
|
||||
onChange={() => setIsDarkMode(!isDarkMode)}
|
||||
type="checkbox"
|
||||
name="toggle"
|
||||
value="toggle"
|
||||
aria-describedby="toggle"
|
||||
checked={isDarkMode}
|
||||
/>
|
||||
<div aria-live="assertive">
|
||||
{isDarkMode ? <Icon name="Sun" /> : <Icon name="Moon" />}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
---
|
||||
import logo from '@images/logo.svg'
|
||||
import Menu from './Menu'
|
||||
import Search from './Search'
|
||||
import ThemeSwitch from './ThemeSwitch'
|
||||
// import Search from './Search'
|
||||
import ThemeSwitch from '../ThemeSwitch/index.astro'
|
||||
import { Logo } from '@images/icons'
|
||||
import styles from './index.module.css'
|
||||
---
|
||||
|
||||
<header role="banner" class={styles.header}>
|
||||
<div class={styles.headerContent}>
|
||||
<a href="/" class={styles.title}>
|
||||
<img src={logo} class={styles.logo} /> kremalicious
|
||||
<Logo class={styles.logo} viewBox="0 0 191 36" /> kremalicious
|
||||
</a>
|
||||
|
||||
<nav aria-label="Menu" class={styles.nav}>
|
||||
<ThemeSwitch />
|
||||
<Search />
|
||||
<!-- <Search /> -->
|
||||
<Menu />
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { ReactElement } from 'react'
|
||||
import Icon from './core/Icon'
|
||||
import styles from './Pagination.module.css'
|
||||
import { ChevronLeft, ChevronRight } from '@images/icons'
|
||||
|
||||
function PageNumber({
|
||||
i,
|
||||
|
@ -34,11 +34,7 @@ function PrevNext({
|
|||
|
||||
return (
|
||||
<a href={link} rel={rel} title={title} className={styles.number}>
|
||||
{prevPagePath ? (
|
||||
<Icon name="ChevronLeft" />
|
||||
) : (
|
||||
<Icon name="ChevronRight" />
|
||||
)}
|
||||
{prevPagePath ? <ChevronLeft /> : <ChevronRight />}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
|
21
src/components/ThemeSwitch/index.astro
Normal file
21
src/components/ThemeSwitch/index.astro
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import { Sun, Moon } from '@images/icons'
|
||||
import styles from './index.module.css'
|
||||
---
|
||||
|
||||
<div class={styles.themeSwitch} title="Toggle Theme" id="theme-toggle">
|
||||
<label for="toggle" class={styles.checkbox} role="presentation">
|
||||
<span class={styles.label}>Toggle Theme</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="theme-toggle"
|
||||
aria-describedby="theme-toggle"
|
||||
/>
|
||||
<div aria-live="assertive">
|
||||
<Sun id="sun" />
|
||||
<Moon id="moon" />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<script src="./script.js"></script>
|
|
@ -23,6 +23,10 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.themeSwitch [hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* hide visually */
|
||||
.checkbox [type='checkbox'],
|
||||
.checkbox .label {
|
57
src/components/ThemeSwitch/script.js
Normal file
57
src/components/ThemeSwitch/script.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
const themeToggle = document.querySelector('#theme-toggle')
|
||||
const themeToggleInput = document.querySelector('#theme-toggle input')
|
||||
const sun = document.querySelector('#sun')
|
||||
const moon = document.querySelector('#moon')
|
||||
const primaryColorScheme = null // "light" | "dark"
|
||||
const currentTheme = localStorage.getItem('theme')
|
||||
|
||||
function getPreferTheme() {
|
||||
if (currentTheme) return currentTheme
|
||||
if (primaryColorScheme) return primaryColorScheme
|
||||
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light'
|
||||
}
|
||||
|
||||
let themeValue = getPreferTheme()
|
||||
|
||||
function setPreference() {
|
||||
localStorage.setItem('theme', themeValue)
|
||||
reflectPreference()
|
||||
}
|
||||
|
||||
function reflectPreference() {
|
||||
document.firstElementChild?.setAttribute('data-theme', themeValue)
|
||||
themeToggle?.setAttribute('aria-label', themeValue)
|
||||
themeToggleInput?.setAttribute('checked', `${themeValue === 'dark'}`)
|
||||
|
||||
if (themeValue === 'dark') {
|
||||
sun?.removeAttribute('hidden')
|
||||
moon?.setAttribute('hidden', '')
|
||||
} else {
|
||||
sun?.setAttribute('hidden', '')
|
||||
moon?.removeAttribute('hidden')
|
||||
}
|
||||
}
|
||||
|
||||
// set early so no page flashes / CSS is made aware
|
||||
reflectPreference()
|
||||
|
||||
window.onload = () => {
|
||||
// set on load so screen readers can get the latest value on the button
|
||||
reflectPreference()
|
||||
|
||||
themeToggle?.addEventListener('click', () => {
|
||||
themeValue = themeValue === 'light' ? 'dark' : 'light'
|
||||
setPreference()
|
||||
})
|
||||
}
|
||||
|
||||
// sync with system changes
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', ({ matches: isDark }) => {
|
||||
themeValue = isDark ? 'dark' : 'light'
|
||||
setPreference()
|
||||
})
|
|
@ -64,7 +64,7 @@
|
|||
margin-left: -1rem;
|
||||
}
|
||||
|
||||
:global(.dark) .inputInput {
|
||||
:global([data-theme='dark']) .inputInput {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
:global(.dark) .currency {
|
||||
:global([data-theme='dark']) .currency {
|
||||
border-right-color: #000;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React, { ReactElement } from 'react'
|
||||
import Clipboard from 'react-clipboard.js'
|
||||
import { Copy } from '@images/icons'
|
||||
import styles from './Copy.module.css'
|
||||
import Icon from './Icon'
|
||||
|
||||
const onCopySuccess = (e: any) => {
|
||||
e.trigger.classList.add(styles.copied)
|
||||
}
|
||||
|
||||
export default function Copy({ text }: { text: string }): ReactElement {
|
||||
export default function CopyAction({ text }: { text: string }): ReactElement {
|
||||
return (
|
||||
<Clipboard
|
||||
data-clipboard-text={text}
|
||||
|
@ -15,7 +15,7 @@ export default function Copy({ text }: { text: string }): ReactElement {
|
|||
onSuccess={(e: ClipboardJS.Event) => onCopySuccess(e)}
|
||||
className={styles.button}
|
||||
>
|
||||
<Icon name="Copy" />
|
||||
<Copy />
|
||||
</Clipboard>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
border-bottom: 1px dashed #fff;
|
||||
}
|
||||
|
||||
:global(.dark) .divider::before {
|
||||
:global([data-theme='dark']) .divider::before {
|
||||
border-bottom-color: var(--brand-grey);
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
stroke: currentcolor;
|
||||
stroke-width: var(--stroke-width);
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
fill: none;
|
||||
vertical-align: baseline;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import Icon from './Icon'
|
||||
|
||||
describe('Icon', () => {
|
||||
it('renders correctly', () => {
|
||||
const { container, rerender } = render(<Icon name={'Compass'} />)
|
||||
expect(container.firstChild.nodeName).toBe('svg')
|
||||
|
||||
rerender(<Icon name={'Download'} />)
|
||||
expect(container.firstChild.nodeName).toBe('svg')
|
||||
|
||||
rerender(<Icon name={'Twitter'} />)
|
||||
expect(container.firstChild.nodeName).toBe('svg')
|
||||
})
|
||||
|
||||
it('does not render with unknown name', () => {
|
||||
const { container } = render(<Icon name={'whatever'} />)
|
||||
expect(container.firstChild).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
|
@ -1,69 +0,0 @@
|
|||
import type { FunctionComponent, ReactElement } from 'react'
|
||||
// https://featherstyles.com
|
||||
// import * as Feather from '@kremalicious/react-feather'
|
||||
import {
|
||||
Aperture,
|
||||
ArrowDownCircle,
|
||||
Camera,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Compass,
|
||||
Copy,
|
||||
Crosshair,
|
||||
Edit,
|
||||
ExternalLink,
|
||||
GitHub,
|
||||
Link,
|
||||
Maximize,
|
||||
Moon,
|
||||
Rss,
|
||||
Search,
|
||||
Sun,
|
||||
Twitter,
|
||||
X
|
||||
} from 'react-feather'
|
||||
import { ReactComponent as Bitcoin } from '../../images/bitcoin.svg'
|
||||
// custom icons
|
||||
import { ReactComponent as Jsonfeed } from '../../images/jsonfeed.svg'
|
||||
import { ReactComponent as Mastodon } from '../../images/mastodon.svg'
|
||||
import { ReactComponent as Stopwatch } from '../../images/stopwatch.svg'
|
||||
import styles from './Icon.module.css'
|
||||
|
||||
const components: {
|
||||
[key: string]: FunctionComponent<React.SVGProps<SVGSVGElement>>
|
||||
} = {
|
||||
Download: ArrowDownCircle,
|
||||
Jsonfeed,
|
||||
Bitcoin,
|
||||
Stopwatch,
|
||||
ArrowDownCircle,
|
||||
Edit,
|
||||
GitHub,
|
||||
Twitter,
|
||||
Rss,
|
||||
Sun,
|
||||
Moon,
|
||||
Compass,
|
||||
X,
|
||||
Copy,
|
||||
Search,
|
||||
ExternalLink,
|
||||
Link,
|
||||
ChevronRight,
|
||||
ChevronLeft,
|
||||
Camera,
|
||||
Aperture,
|
||||
Maximize,
|
||||
Crosshair,
|
||||
Mastodon
|
||||
}
|
||||
|
||||
const Icon = ({ name, ...props }: { name: string }): ReactElement | null => {
|
||||
const IconMapped = components[name]
|
||||
// const IconFeather = (Feather as any)[name]
|
||||
if (!IconMapped) return null
|
||||
|
||||
return <IconMapped className={styles.icon} {...props} />
|
||||
}
|
||||
|
||||
export default Icon
|
|
@ -28,7 +28,7 @@
|
|||
transform: translate3d(0, calc(var(--spacer) * 2), 0);
|
||||
}
|
||||
|
||||
:global(.dark) .document {
|
||||
:global([data-theme='dark']) .document {
|
||||
border-top-color: rgba(255 255 255 / 5%);
|
||||
box-shadow:
|
||||
0 1px 8px rgba(0 7 8 / 30%),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
import Time from '@components/core/Time.astro'
|
||||
import Icon from '../../core/Icon'
|
||||
import styles from './Title.module.css'
|
||||
import { ExternalLink } from '@images/icons'
|
||||
|
||||
type Props = {
|
||||
linkurl?: string
|
||||
|
@ -22,7 +22,7 @@ const linkHostname = linkurl ? new URL(linkurl).hostname : null
|
|||
class={`${styles.title} ${styles.titleLink} ${className && className}`}
|
||||
>
|
||||
<a href={linkurl} title={`Go to source: ${linkurl}`}>
|
||||
{title} <Icon name="ExternalLink" />
|
||||
{title} <ExternalLink />
|
||||
</a>
|
||||
</h1>
|
||||
<div class={styles.linkurl}>{linkHostname}</div>
|
||||
|
|
|
@ -19,11 +19,15 @@ kbd {
|
|||
rgba(0, 0, 0, 0)
|
||||
);
|
||||
background-repeat: repeat-x;
|
||||
box-shadow: 0 2px 0 #bbb, 0 3px 1px #999, 0 3px 0 #bbb, inset 0 1px 1px #fff,
|
||||
box-shadow:
|
||||
0 2px 0 #bbb,
|
||||
0 3px 1px #999,
|
||||
0 3px 0 #bbb,
|
||||
inset 0 1px 1px #fff,
|
||||
inset 0 -1px 3px #ccc;
|
||||
}
|
||||
|
||||
kbd.dark {
|
||||
kbd[data-theme='dark'] {
|
||||
color: #eee;
|
||||
text-shadow: 0 -1px 0 #000;
|
||||
border-color: #000;
|
||||
|
@ -34,7 +38,10 @@ kbd.dark {
|
|||
rgba(0, 0, 0, 0)
|
||||
);
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: 0 2px 0 #000, 0 3px 1px #999, inset 0 1px 1px #aaa,
|
||||
box-shadow:
|
||||
0 2px 0 #000,
|
||||
0 3px 1px #999,
|
||||
inset 0 1px 1px #aaa,
|
||||
inset 0 -1px 3px #272727;
|
||||
}
|
||||
|
||||
|
@ -46,7 +53,9 @@ kbd.ios {
|
|||
background-color: #b7b7bc;
|
||||
background-image: linear-gradient(to bottom, #efeff0, #b7b7bc);
|
||||
background-repeat: repeat-x;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 2px 3px rgba(0, 0, 0, 0.1),
|
||||
box-shadow:
|
||||
0 1px 2px rgba(0, 0, 0, 0.6),
|
||||
0 2px 3px rgba(0, 0, 0, 0.1),
|
||||
inset 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
|
@ -59,24 +68,33 @@ kbd.android {
|
|||
border-radius: 3px;
|
||||
background-clip: padding-box;
|
||||
background: #5e5e5e;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3), 0 1px 0 #444, inset 0 1px 0 #868686;
|
||||
box-shadow:
|
||||
0 2px 2px rgba(0, 0, 0, 0.3),
|
||||
0 1px 0 #444,
|
||||
inset 0 1px 0 #868686;
|
||||
}
|
||||
|
||||
kbd.android.dark {
|
||||
kbd.android[data-theme='dark'] {
|
||||
background: #222;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.7), 0 1px 0 #444, inset 0 1px 0 #505050;
|
||||
box-shadow:
|
||||
0 2px 2px rgba(0, 0, 0, 0.7),
|
||||
0 1px 0 #444,
|
||||
inset 0 1px 0 #505050;
|
||||
}
|
||||
|
||||
kbd.android.color {
|
||||
background: #083c5b;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.7), 0 1px 0 #444, inset 0 1px 0 #36647b;
|
||||
box-shadow:
|
||||
0 2px 2px rgba(0, 0, 0, 0.7),
|
||||
0 1px 0 #444,
|
||||
inset 0 1px 0 #36647b;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'RobotoRegular';
|
||||
src: url('/media/Roboto-Regular-webfont.eot');
|
||||
src: url('/media/Roboto-Regular-webfont.eot?#iefix')
|
||||
format('embedded-opentype'),
|
||||
src:
|
||||
url('/media/Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('/media/Roboto-Regular-webfont.woff') format('woff'),
|
||||
url('/media/Roboto-Regular-webfont.ttf') format('truetype'),
|
||||
url('/media/Roboto-Regular-webfont.svg#RobotoRegular') format('svg');
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { ReactElement } from 'react'
|
||||
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
||||
import Icon from '../../components/core/Icon'
|
||||
import styles from './Actions.module.css'
|
||||
|
||||
interface ActionProps {
|
||||
|
@ -14,7 +13,7 @@ interface ActionProps {
|
|||
const Action = ({ title, text, url, icon, onClick }: ActionProps) => {
|
||||
return (
|
||||
<a className={styles.action} href={url} onClick={onClick}>
|
||||
<Icon name={icon} />
|
||||
{/* <Icon name={icon} /> */}
|
||||
<h1 className={styles.actionTitle}>{title}</h1>
|
||||
<p className={styles.actionText}>{text}</p>
|
||||
</a>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { ReactElement } from 'react'
|
||||
import Icon from '../../components/core/Icon'
|
||||
import styles from './LinkActions.module.css'
|
||||
import stylesMore from './More.module.css'
|
||||
import { ExternalLink, Link } from '@images/icons'
|
||||
|
||||
const PostLinkActions = ({
|
||||
linkurl,
|
||||
|
@ -12,10 +12,10 @@ const PostLinkActions = ({
|
|||
}): ReactElement => (
|
||||
<div className={styles.postLinkActions}>
|
||||
<a className={stylesMore.postMore} href={linkurl}>
|
||||
Go to source <Icon name="ExternalLink" />
|
||||
Go to source <ExternalLink />
|
||||
</a>
|
||||
<a href={slug} rel="tooltip" title="Permalink">
|
||||
<Icon name="Link" />
|
||||
<Link />
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { ReactElement } from 'react'
|
||||
import Icon from '../../components/core/Icon'
|
||||
import { ChevronRight } from '@images/icons'
|
||||
import styles from './More.module.css'
|
||||
|
||||
const PostMore = ({
|
||||
|
@ -11,7 +11,7 @@ const PostMore = ({
|
|||
}): ReactElement => (
|
||||
<a className={styles.postMore} href={to}>
|
||||
{children}
|
||||
<Icon name="ChevronRight" />
|
||||
<ChevronRight />
|
||||
</a>
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { ReactElement } from 'react'
|
||||
import Icon from '../../components/core/Icon'
|
||||
import styles from './PrevNext.module.css'
|
||||
import { ChevronLeft, ChevronRight } from '@images/icons'
|
||||
|
||||
interface Node {
|
||||
title: string
|
||||
|
@ -17,7 +17,7 @@ const PrevNext = ({ prev, next }: PrevNextProps): ReactElement => (
|
|||
<div>
|
||||
{prev && (
|
||||
<a href={prev.slug}>
|
||||
<Icon name="ChevronLeft" />
|
||||
<ChevronLeft />
|
||||
<p className={styles.label}>Newer</p>
|
||||
<h3 className={styles.title}>{prev.title}</h3>
|
||||
</a>
|
||||
|
@ -28,7 +28,7 @@ const PrevNext = ({ prev, next }: PrevNextProps): ReactElement => (
|
|||
<a href={next.slug}>
|
||||
<p className={styles.label}>Older</p>
|
||||
<h3 className={styles.title}>{next.title}</h3>
|
||||
<Icon name="ChevronRight" />
|
||||
<ChevronRight />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -5,9 +5,9 @@ import { RainbowKitProvider } from '@rainbow-me/rainbowkit'
|
|||
import { chains, theme, wagmiConfig } from '../lib/rainbowkit'
|
||||
import Copy from '../components/core/Copy'
|
||||
import Meta, { HeadMetaProps } from '../components/core/HeadMeta'
|
||||
import Icon from '../components/core/Icon'
|
||||
import { useSiteMetadata } from '../hooks/useSiteMetadata'
|
||||
import styles from './thanks.module.css'
|
||||
import { ChevronLeft } from '@images/icons'
|
||||
|
||||
const meta: Partial<HeadMetaProps> = {
|
||||
title: `Say Thanks`
|
||||
|
@ -32,7 +32,7 @@ const BackButton = () => (
|
|||
className={`link ${styles.buttonBack}`}
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
<Icon name="ChevronLeft" /> Go Back
|
||||
<ChevronLeft /> Go Back
|
||||
</button>
|
||||
)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import PhotoTeaser from '@components/PhotoTeaser.astro'
|
|||
import type { CollectionEntry } from 'astro:content'
|
||||
|
||||
const articles = await loadAndFormatCollection('articles')
|
||||
const links = await loadAndFormatCollection('links')
|
||||
// const links = await loadAndFormatCollection('links')
|
||||
const photos = await loadAndFormatCollection('photos')
|
||||
---
|
||||
|
||||
|
|
|
@ -23,12 +23,12 @@
|
|||
background: var(--alert-info);
|
||||
}
|
||||
|
||||
.dark .alert-info {
|
||||
[data-theme='dark'] .alert-info {
|
||||
color: var(--alert-info);
|
||||
background: rgba(248 241 227 / 20%);
|
||||
}
|
||||
|
||||
/* .dark & {
|
||||
/* [data-theme="dark"] & {
|
||||
color: darken($alert-info, 40%);
|
||||
background: darken($alert-info, 85%);
|
||||
border-color: darken($alert-info, 90%);
|
||||
|
|
|
@ -100,11 +100,11 @@ a.btn-primary {
|
|||
background-size: 100%;
|
||||
}
|
||||
|
||||
.dark .btn[class*='icon-']::before {
|
||||
[data-theme='dark'] .btn[class*='icon-']::before {
|
||||
filter: invert(0.75);
|
||||
}
|
||||
|
||||
.dark .btn.btn-primary[class*='icon-']::before {
|
||||
[data-theme='dark'] .btn.btn-primary[class*='icon-']::before {
|
||||
filter: invert(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
--easing: cubic-bezier(0.75, 0, 0.08, 100%);
|
||||
}
|
||||
|
||||
.dark {
|
||||
[data-theme='dark'] {
|
||||
--body-background-color: #161a1b;
|
||||
--box-background-color: rgba(255 255 255 / 3%);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user