chore: layout

This commit is contained in:
enscribe 2024-09-10 12:51:03 -07:00
parent b9561ad2d0
commit 230dca64ca
No known key found for this signature in database
GPG key ID: 9BBD5C4114E25322
27 changed files with 446 additions and 339 deletions

View file

@ -8,10 +8,19 @@ import remarkEmoji from 'remark-emoji'
import remarkMath from 'remark-math' import remarkMath from 'remark-math'
import remarkToc from 'remark-toc' import remarkToc from 'remark-toc'
import react from '@astrojs/react'
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
site: 'https://astro-micro-academic.vercel.app', site: 'https://astro-micro-academic.vercel.app',
integrations: [tailwind(), sitemap(), mdx()], integrations: [
tailwind({
applyBaseStyles: false,
}),
sitemap(),
mdx(),
react(),
],
markdown: { markdown: {
shikiConfig: { shikiConfig: {
theme: 'css-variables', theme: 'css-variables',
@ -19,5 +28,11 @@ export default defineConfig({
rehypePlugins: [rehypeHeadingIds, rehypeKatex], rehypePlugins: [rehypeHeadingIds, rehypeKatex],
remarkPlugins: [remarkToc, remarkMath, remarkEmoji], remarkPlugins: [remarkToc, remarkMath, remarkEmoji],
}, },
server: { port: 1234, host: true }, server: {
port: 1234,
host: true,
},
devToolbar: {
enabled: false,
},
}) })

20
components.json Normal file
View file

@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/styles/global.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}

208
package-lock.json generated
View file

@ -11,20 +11,29 @@
"@astrojs/check": "^0.7.0", "@astrojs/check": "^0.7.0",
"@astrojs/markdown-remark": "^5.2.0", "@astrojs/markdown-remark": "^5.2.0",
"@astrojs/mdx": "^3.1.2", "@astrojs/mdx": "^3.1.2",
"@astrojs/react": "^3.6.2",
"@astrojs/rss": "^4.0.7", "@astrojs/rss": "^4.0.7",
"@astrojs/sitemap": "^3.1.6", "@astrojs/sitemap": "^3.1.6",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@fontsource/geist-mono": "^5.0.3", "@fontsource/geist-mono": "^5.0.3",
"@fontsource/geist-sans": "^5.0.3", "@fontsource/geist-sans": "^5.0.3",
"@radix-ui/react-icons": "^1.3.0",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"astro": "^4.11.3", "astro": "^4.11.3",
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.439.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rehype-katex": "^7.0.0", "rehype-katex": "^7.0.0",
"remark-emoji": "^5.0.1", "remark-emoji": "^5.0.1",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
"remark-toc": "^9.0.0", "remark-toc": "^9.0.0",
"tailwind-merge": "^2.3.0", "tailwind-merge": "^2.5.2",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.3",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.4.5" "typescript": "^5.4.5"
}, },
"devDependencies": { "devDependencies": {
@ -222,6 +231,24 @@
"node": "^18.17.1 || ^20.3.0 || >=21.0.0" "node": "^18.17.1 || ^20.3.0 || >=21.0.0"
} }
}, },
"node_modules/@astrojs/react": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/@astrojs/react/-/react-3.6.2.tgz",
"integrity": "sha512-fK29lYI7zK/KG4ZBy956x4dmauZcZ18osFkuyGa8r3gmmCQa2NZ9XNu9WaVYEUm0j89f4Gii4tbxLoyM8nk2MA==",
"dependencies": {
"@vitejs/plugin-react": "^4.3.1",
"ultrahtml": "^1.5.3"
},
"engines": {
"node": "^18.17.1 || ^20.3.0 || >=21.0.0"
},
"peerDependencies": {
"@types/react": "^17.0.50 || ^18.0.21",
"@types/react-dom": "^17.0.17 || ^18.0.6",
"react": "^17.0.2 || ^18.0.0 || ^19.0.0-beta",
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0-beta"
}
},
"node_modules/@astrojs/rss": { "node_modules/@astrojs/rss": {
"version": "4.0.7", "version": "4.0.7",
"resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.7.tgz", "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.7.tgz",
@ -593,6 +620,34 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/@babel/plugin-transform-react-jsx-self": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz",
"integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-react-jsx-source": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz",
"integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.24.7", "version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
@ -1705,6 +1760,14 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@radix-ui/react-icons": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
"integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==",
"peerDependencies": {
"react": "^16.x || ^17.x || ^18.x"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.18.1", "version": "4.18.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz",
@ -2082,6 +2145,28 @@
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/@types/prop-types": {
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
},
"node_modules/@types/react": {
"version": "18.3.5",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz",
"integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
"version": "18.3.0",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
"integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/sax": { "node_modules/@types/sax": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
@ -2108,6 +2193,24 @@
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/@vitejs/plugin-react": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz",
"integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==",
"dependencies": {
"@babel/core": "^7.24.5",
"@babel/plugin-transform-react-jsx-self": "^7.24.5",
"@babel/plugin-transform-react-jsx-source": "^7.24.1",
"@types/babel__core": "^7.20.5",
"react-refresh": "^0.14.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"peerDependencies": {
"vite": "^4.2.0 || ^5.0.0"
}
},
"node_modules/@volar/kit": { "node_modules/@volar/kit": {
"version": "2.4.0-alpha.15", "version": "2.4.0-alpha.15",
"resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.0-alpha.15.tgz", "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.0-alpha.15.tgz",
@ -2832,6 +2935,25 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/class-variance-authority": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz",
"integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==",
"dependencies": {
"clsx": "2.0.0"
},
"funding": {
"url": "https://joebell.co.uk"
}
},
"node_modules/class-variance-authority/node_modules/clsx": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
"engines": {
"node": ">=6"
}
},
"node_modules/cli-boxes": { "node_modules/cli-boxes": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
@ -2980,7 +3102,6 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -3121,6 +3242,11 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.3.5", "version": "4.3.5",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
@ -4619,6 +4745,17 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@ -4628,6 +4765,14 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/lucide-react": {
"version": "0.439.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.439.0.tgz",
"integrity": "sha512-PafSWvDTpxdtNEndS2HIHxcNAbd54OaqSYJO90/b63rab2HWYqDbH194j0i82ZFdWOAcf0AHinRykXRRK2PJbw==",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.10", "version": "0.30.10",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
@ -6693,6 +6838,37 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
},
"peerDependencies": {
"react": "^18.3.1"
}
},
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/read-cache": { "node_modules/read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -7161,6 +7337,14 @@
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
"dependencies": {
"loose-envify": "^1.1.0"
}
},
"node_modules/section-matter": { "node_modules/section-matter": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
@ -7586,10 +7770,9 @@
} }
}, },
"node_modules/tailwind-merge": { "node_modules/tailwind-merge": {
"version": "2.4.0", "version": "2.5.2",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.4.0.tgz", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz",
"integrity": "sha512-49AwoOQNKdqKPd9CViyH5wJoSKsCDjUlzL8DxuGp3P1FsGY36NJDAa18jLZcaHAUUuTj+JB8IAo8zWgBNvBF7A==", "integrity": "sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/dcastil" "url": "https://github.com/sponsors/dcastil"
@ -7632,6 +7815,14 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/tailwindcss-animate": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
"integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders"
}
},
"node_modules/tailwindcss/node_modules/glob-parent": { "node_modules/tailwindcss/node_modules/glob-parent": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@ -7801,6 +7992,11 @@
"semver": "^7.3.8" "semver": "^7.3.8"
} }
}, },
"node_modules/ultrahtml": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.5.3.tgz",
"integrity": "sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg=="
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "5.26.5", "version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",

View file

@ -15,20 +15,29 @@
"@astrojs/check": "^0.7.0", "@astrojs/check": "^0.7.0",
"@astrojs/markdown-remark": "^5.2.0", "@astrojs/markdown-remark": "^5.2.0",
"@astrojs/mdx": "^3.1.2", "@astrojs/mdx": "^3.1.2",
"@astrojs/react": "^3.6.2",
"@astrojs/rss": "^4.0.7", "@astrojs/rss": "^4.0.7",
"@astrojs/sitemap": "^3.1.6", "@astrojs/sitemap": "^3.1.6",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@fontsource/geist-mono": "^5.0.3", "@fontsource/geist-mono": "^5.0.3",
"@fontsource/geist-sans": "^5.0.3", "@fontsource/geist-sans": "^5.0.3",
"@radix-ui/react-icons": "^1.3.0",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"astro": "^4.11.3", "astro": "^4.11.3",
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.439.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rehype-katex": "^7.0.0", "rehype-katex": "^7.0.0",
"remark-emoji": "^5.0.1", "remark-emoji": "^5.0.1",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
"remark-toc": "^9.0.0", "remark-toc": "^9.0.0",
"tailwind-merge": "^2.3.0", "tailwind-merge": "^2.5.2",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.3",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.4.5" "typescript": "^5.4.5"
}, },
"devDependencies": { "devDependencies": {

View file

@ -2,4 +2,4 @@
--- ---
<div class="mx-auto max-w-screen-md px-3"><slot /></div> <div class="mx-auto max-w-screen-md"><slot /></div>

View file

@ -5,7 +5,7 @@ import BackToTop from '@components/BackToTop.astro'
import SocialIcons from './SocialIcons.astro' import SocialIcons from './SocialIcons.astro'
--- ---
<footer class="animate"> <footer>
<Container> <Container>
<div class="my-2 flex justify-between"> <div class="my-2 flex justify-between">
<SocialIcons icon_size={'text-xl'} /> <SocialIcons icon_size={'text-xl'} />
@ -16,78 +16,6 @@ import SocialIcons from './SocialIcons.astro'
&copy; {new Date().getFullYear()} • {SITE.TITLE} 👀<br /> &copy; {new Date().getFullYear()} • {SITE.TITLE} 👀<br />
Built with Astro Built with Astro
</div> </div>
<div class="flex flex-wrap items-center gap-1.5">
<button
id="light-theme-button"
aria-label="Light theme"
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
>
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</button>
<button
id="dark-theme-button"
aria-label="Dark theme"
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
>
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
</button>
<button
id="system-theme-button"
aria-label="System theme"
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
>
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
<line x1="8" y1="21" x2="16" y2="21"></line>
<line x1="12" y1="17" x2="12" y2="21"></line>
</svg>
</button>
</div>
</div> </div>
</Container> </Container>
</footer> </footer>

View file

@ -1,6 +1,5 @@
--- ---
import '../styles/global.css' import '../styles/global.css'
import { ViewTransitions } from 'astro:transitions'
import '@fontsource/geist-sans' import '@fontsource/geist-sans'
import '@fontsource/geist-mono' import '@fontsource/geist-mono'
@ -16,46 +15,31 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site)
const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
--- ---
<!-- Global Metadata -->
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🍄</text></svg>"
/>
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<!-- Canonical URL -->
<link rel="canonical" href={canonicalURL} /> <link rel="canonical" href={canonicalURL} />
<!-- Primary Meta Tags -->
<title>{title}</title> <title>{title}</title>
<meta name="title" content={title} /> <meta name="title" content={title} />
<meta name="description" content={description} /> <meta name="description" content={description} />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} /> <meta property="og:url" content={Astro.url} />
<meta property="og:title" content={title} /> <meta property="og:title" content={title} />
<meta property="og:description" content={description} /> <meta property="og:description" content={description} />
<meta property="og:image" content={new URL(image, Astro.url)} /> <meta property="og:image" content={new URL(image, Astro.url)} />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={Astro.url} /> <meta property="twitter:url" content={Astro.url} />
<meta property="twitter:title" content={title} /> <meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} /> <meta property="twitter:description" content={description} />
<meta property="twitter:image" content={new URL(image, Astro.url)} /> <meta property="twitter:image" content={new URL(image, Astro.url)} />
<ViewTransitions />
<script is:inline> <script is:inline>
function init() { function init() {
preloadTheme()
onScroll() onScroll()
animate()
updateThemeButtons()
addCopyCodeButtons() addCopyCodeButtons()
const backToTop = document.getElementById('back-to-top') const backToTop = document.getElementById('back-to-top')
@ -64,77 +48,9 @@ const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
const backToPrev = document.getElementById('back-to-prev') const backToPrev = document.getElementById('back-to-prev')
backToPrev?.addEventListener('click', () => window.history.back()) backToPrev?.addEventListener('click', () => window.history.back())
const lightThemeButton = document.getElementById('light-theme-button')
lightThemeButton?.addEventListener('click', () => {
localStorage.setItem('theme', 'light')
toggleTheme(false)
updateThemeButtons()
})
const darkThemeButton = document.getElementById('dark-theme-button')
darkThemeButton?.addEventListener('click', () => {
localStorage.setItem('theme', 'dark')
toggleTheme(true)
updateThemeButtons()
})
const systemThemeButton = document.getElementById('system-theme-button')
systemThemeButton?.addEventListener('click', () => {
localStorage.setItem('theme', 'system')
toggleTheme(window.matchMedia('(prefers-color-scheme: dark)').matches)
updateThemeButtons()
})
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (event) => {
if (localStorage.theme === 'system') {
toggleTheme(event.matches)
}
})
document.addEventListener('scroll', onScroll) document.addEventListener('scroll', onScroll)
} }
function updateThemeButtons() {
const theme = localStorage.getItem('theme')
const lightThemeButton = document.getElementById('light-theme-button')
const darkThemeButton = document.getElementById('dark-theme-button')
const systemThemeButton = document.getElementById('system-theme-button')
function removeActiveButtonTheme(button) {
button?.classList.remove('bg-black/5')
button?.classList.remove('dark:bg-white/5')
}
function addActiveButtonTheme(button) {
button?.classList.add('bg-black/5')
button?.classList.add('dark:bg-white/5')
}
removeActiveButtonTheme(lightThemeButton)
removeActiveButtonTheme(darkThemeButton)
removeActiveButtonTheme(systemThemeButton)
if (theme === 'light') {
addActiveButtonTheme(lightThemeButton)
} else if (theme === 'dark') {
addActiveButtonTheme(darkThemeButton)
} else {
addActiveButtonTheme(systemThemeButton)
}
}
function animate() {
const animateElements = document.querySelectorAll('.animate')
animateElements.forEach((element, index) => {
setTimeout(() => {
element.classList.add('show')
}, index * 100)
})
}
function onScroll() { function onScroll() {
if (window.scrollY > 0) { if (window.scrollY > 0) {
document.documentElement.classList.add('scrolled') document.documentElement.classList.add('scrolled')
@ -151,44 +67,6 @@ const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
}) })
} }
function toggleTheme(dark) {
const css = document.createElement('style')
css.appendChild(
document.createTextNode(
`* {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
-ms-transition: none !important;
transition: none !important;
}
`,
),
)
document.head.appendChild(css)
if (dark) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
window.getComputedStyle(css).opacity
document.head.removeChild(css)
}
function preloadTheme() {
const userTheme = localStorage.theme
if (userTheme === 'light' || userTheme === 'dark') {
toggleTheme(true) // set default to dark theme
} else {
toggleTheme(window.matchMedia('(prefers-color-scheme: dark)').matches)
}
}
function addCopyCodeButtons() { function addCopyCodeButtons() {
let copyButtonLabel = '📋' let copyButtonLabel = '📋'
let codeBlocks = Array.from(document.querySelectorAll('pre')) let codeBlocks = Array.from(document.querySelectorAll('pre'))
@ -228,5 +106,4 @@ const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
document.addEventListener('DOMContentLoaded', () => init()) document.addEventListener('DOMContentLoaded', () => init())
document.addEventListener('astro:after-swap', () => init()) document.addEventListener('astro:after-swap', () => init())
preloadTheme()
</script> </script>

View file

@ -2,33 +2,30 @@
import Container from '@components/Container.astro' import Container from '@components/Container.astro'
import Link from '@components/Link.astro' import Link from '@components/Link.astro'
import { SITE } from '@consts' import { SITE } from '@consts'
const items = [
{ href: '/blog', label: 'blog' },
{ href: '/authors', label: 'authors' },
{ href: '/about', label: 'about' },
{ href: '/tags', label: 'tags' },
]
--- ---
<header transition:persist> <header class="sticky top-0 z-10" transition:persist>
<Container> <Container>
<div class="flex flex-wrap justify-between gap-y-2"> <div class="flex items-center justify-between py-4">
<Link href="/" underline={false}> <Link href="/" underline={false}>
<div class="font-semibold"> {SITE.TITLE}<span class="ml-1">🍄</span>
{SITE.TITLE}&nbsp;🍄
</div>
</Link> </Link>
<nav class="flex items-center gap-1 text-sm"> <nav class="flex items-center space-x-1 text-sm">
<Link href="/blog">blog</Link> {
<span> items.map((item, index) => (
{`/`} <>
</span> <Link href={item.href}>{item.label}</Link>
<Link href="/authors">authors</Link> {index < items.length - 1 && <span class="text-gray-400">/</span>}
<span> </>
{`/`} ))
</span> }
<Link href="/about">about</Link>
<span>
{`/`}
</span>
<Link href="/tags">tags</Link>
<span>
{`/`}
</span>
</nav> </nav>
</div> </div>
</Container> </Container>

View file

@ -1,17 +1,17 @@
--- ---
import type { CollectionEntry } from "astro:content"; import type { CollectionEntry } from 'astro:content'
import { Image } from "astro:assets"; import { Image } from 'astro:assets'
type Props = { type Props = {
member: CollectionEntry<"authors">; member: CollectionEntry<'authors'>
}; }
const { member } = Astro.props; const { member } = Astro.props
const { name, avatar, bio } = member.data; const { name, avatar, bio } = member.data
--- ---
<div <div
class="animate not-prose flex flex-col sm:flex-row size-full sm:items-center gap-4 overflow-hidden rounded-xl border border-foreground bg-background p-6 hover:bg-secondary" class="not-prose flex size-full flex-col gap-4 overflow-hidden rounded-xl border border-foreground bg-background p-6 hover:bg-secondary sm:flex-row sm:items-center"
> >
<Image <Image
src={avatar} src={avatar}

View file

@ -27,10 +27,7 @@ function buildToc(headings: Heading[]) {
} }
--- ---
<details <details open class="rounded-lg border border-black/15 dark:border-white/20">
open
class="animate rounded-lg border border-black/15 dark:border-white/20"
>
<summary>Table of Contents</summary> <summary>Table of Contents</summary>
<nav class=""> <nav class="">
<ul class="py-3"> <ul class="py-3">

View file

@ -1,8 +1,8 @@
--- ---
name: "enscribe" name: 'enscribe'
avatar: "https://gravatar.com/avatar/9bfdc4ec972793cf05cb91efce5f4aaaec2a0da1bf4ec34dad0913f1d845faf6.webp?size=256" avatar: 'https://gravatar.com/avatar/9bfdc4ec972793cf05cb91efce5f4aaaec2a0da1bf4ec34dad0913f1d845faf6.webp?size=256'
bio: "d(-_-)b" bio: 'd(-_-)b'
website: "https://enscribe.dev" website: 'https://enscribe.dev'
twitter: "enscry" twitter: 'enscry'
github: "jktrn" github: 'jktrn'
--- ---

View file

@ -62,7 +62,7 @@ When developing you can continue to use `npm run dev` and Pagefind will use the
## UI enhancements 🎨 ## UI enhancements 🎨
- Elements are styled and animate on focus - Elements are styled and on focus
- Increased contrast in light mode - Increased contrast in light mode
- Active theme is indicated by theme buttons - Active theme is indicated by theme buttons
- Separate syntax highlight themes for light and dark mode - Separate syntax highlight themes for light and dark mode

View file

@ -1,7 +1,7 @@
import { defineCollection, reference, z } from "astro:content"; import { defineCollection, reference, z } from 'astro:content'
const blog = defineCollection({ const blog = defineCollection({
type: "content", type: 'content',
schema: z.object({ schema: z.object({
title: z.string(), title: z.string(),
description: z.string(), description: z.string(),
@ -9,12 +9,12 @@ const blog = defineCollection({
draft: z.boolean().optional(), draft: z.boolean().optional(),
tags: z.array(z.string()).optional(), tags: z.array(z.string()).optional(),
author: z.union([reference("authors"), z.string()]).optional(), author: z.union([reference('authors'), z.string()]).optional(),
}), }),
}); })
const authors = defineCollection({ const authors = defineCollection({
type: "content", type: 'content',
schema: z.object({ schema: z.object({
name: z.string(), name: z.string(),
avatar: z.string().url(), avatar: z.string().url(),
@ -26,6 +26,6 @@ const authors = defineCollection({
mail: z.string().email().optional(), mail: z.string().email().optional(),
discord: z.string().optional(), discord: z.string().optional(),
}), }),
}); })
export const collections = { blog, authors }; export const collections = { blog, authors }

View file

@ -4,6 +4,7 @@ import Header from '@components/Header.astro'
import Footer from '@components/Footer.astro' import Footer from '@components/Footer.astro'
import { SITE } from '@consts' import { SITE } from '@consts'
import '../styles/katex.css' import '../styles/katex.css'
import { ViewTransitions } from 'astro:transitions'
type Props = { type Props = {
title: string title: string
@ -17,10 +18,13 @@ const { title, description } = Astro.props
<html lang="en"> <html lang="en">
<head> <head>
<Head title={`${title} | ${SITE.TITLE}`} description={description} /> <Head title={`${title} | ${SITE.TITLE}`} description={description} />
<ViewTransitions />
</head> </head>
<body> <body
class="box-border flex h-fit min-h-screen flex-col px-4 font-sans antialiased"
>
<Header /> <Header />
<main> <main class="flex-grow">
<slot /> <slot />
</main> </main>
<Footer /> <Footer />

View file

@ -9,10 +9,10 @@ import { SITE } from '@consts'
<Layout title="404" description={SITE.DESCRIPTION}> <Layout title="404" description={SITE.DESCRIPTION}>
<Container> <Container>
<div class="mt-16 grid place-items-center gap-3"> <div class="mt-16 grid place-items-center gap-3">
<h4 class="animate text-2xl font-semibold text-black dark:text-white"> <h4 class="text-2xl font-semibold text-black dark:text-white">
404: Page not found 404: Page not found
</h4> </h4>
<span class="animate"> <span>
<BackToPrevious href="/">Go to home page</BackToPrevious> <BackToPrevious href="/">Go to home page</BackToPrevious>
</span> </span>
</div> </div>

View file

@ -8,8 +8,8 @@ import { Image } from 'astro:assets'
<Layout title="About" description="About"> <Layout title="About" description="About">
<Container> <Container>
<div class="space-y-10"> <div class="space-y-10">
<div class="animate font-semibold text-black dark:text-white">About</div> <div class="font-semibold text-black dark:text-white">About</div>
<section class="animate not-prose flex flex-col gap-4 text-justify"> <section class="not-prose flex flex-col gap-4 text-justify">
<p class="text-justify"> <p class="text-justify">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolores porro Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolores porro
hic minima incidunt explicabo obcaecati consectetur consequuntur at hic minima incidunt explicabo obcaecati consectetur consequuntur at

View file

@ -1,33 +1,33 @@
--- ---
import { type CollectionEntry, getCollection, getEntry } from "astro:content"; import { type CollectionEntry, getCollection, getEntry } from 'astro:content'
import Layout from "@layouts/Layout.astro"; import Layout from '@layouts/Layout.astro'
import MemberCard from "@components/MemberCard.astro"; import MemberCard from '@components/MemberCard.astro'
export async function getStaticPaths() { export async function getStaticPaths() {
const authors = await getCollection("authors"); const authors = await getCollection('authors')
return authors.map((member) => ({ return authors.map((member) => ({
params: { slug: member.slug }, params: { slug: member.slug },
props: { member }, props: { member },
})); }))
} }
type Props = { type Props = {
member: CollectionEntry<"authors">; member: CollectionEntry<'authors'>
}; }
const { member } = Astro.props; const { member } = Astro.props
const allPosts = await getCollection("blog"); const allPosts = await getCollection('blog')
const memberPosts = allPosts const memberPosts = allPosts
.filter((post) => { .filter((post) => {
if (typeof post.data.author === 'string') { if (typeof post.data.author === 'string') {
return post.data.author === member.data.name && !post.data.draft; return post.data.author === member.data.name && !post.data.draft
} else if (post.data.author && 'slug' in post.data.author) { } else if (post.data.author && 'slug' in post.data.author) {
return post.data.author.slug === member.slug && !post.data.draft; return post.data.author.slug === member.slug && !post.data.draft
} }
return false; return false
}) })
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()); .sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
--- ---
<Layout <Layout

View file

@ -1,16 +1,14 @@
--- ---
import { getCollection } from "astro:content"; import { getCollection } from 'astro:content'
import Layout from "@layouts/Layout.astro"; import Layout from '@layouts/Layout.astro'
import MemberCard from "@components/MemberCard.astro"; import MemberCard from '@components/MemberCard.astro'
const authors = await getCollection("authors"); const authors = await getCollection('authors')
--- ---
<Layout title="authors" description="authors"> <Layout title="authors" description="authors">
<section> <section>
<ul <ul class="not-prose grid grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3">
class="animate not-prose grid grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3"
>
{ {
authors.map((member) => ( authors.map((member) => (
<li> <li>

View file

@ -53,7 +53,7 @@ const { Content, headings } = await post.render()
<BackToPrevious href="/blog">Back to blog</BackToPrevious> <BackToPrevious href="/blog">Back to blog</BackToPrevious>
</div> </div>
<div class="my-10 space-y-4"> <div class="my-10 space-y-4">
<div class="animate flex items-center gap-1.5"> <div class="flex items-center gap-1.5">
<div class="font-base text-sm"> <div class="font-base text-sm">
<FormattedDate date={post.data.date} /> <FormattedDate date={post.data.date} />
</div> </div>
@ -62,7 +62,7 @@ const { Content, headings } = await post.render()
{readingTime(post.body)} {readingTime(post.body)}
</div> </div>
</div> </div>
<h1 class="animate text-4xl font-semibold text-black dark:text-white"> <h1 class="text-4xl font-semibold text-black dark:text-white">
{post.data.title} {post.data.title}
</h1> </h1>
<div class="font-base text-sm"> <div class="font-base text-sm">

View file

@ -30,7 +30,7 @@ const years = Object.keys(posts).sort((a, b) => parseInt(b) - parseInt(a))
<div class="space-y-4"> <div class="space-y-4">
{ {
years.map((year) => ( years.map((year) => (
<section class="animate space-y-4"> <section class="space-y-4">
<div class="font-semibold text-black dark:text-white">{year}</div> <div class="font-semibold text-black dark:text-white">{year}</div>
<div> <div>
<ul class="not-prose flex flex-col gap-4"> <ul class="not-prose flex flex-col gap-4">

View file

@ -16,7 +16,7 @@ const blog = (await getCollection('blog'))
<Layout title="Home" description="Home"> <Layout title="Home" description="Home">
<Container> <Container>
<section class="animate space-y-6"> <section class="space-y-6">
<div class="flex flex-wrap items-center justify-between gap-y-2"> <div class="flex flex-wrap items-center justify-between gap-y-2">
<h2 class="font-semibold text-black dark:text-white">Latest posts</h2> <h2 class="font-semibold text-black dark:text-white">Latest posts</h2>
<Link href="/blog"> See all posts </Link> <Link href="/blog"> See all posts </Link>

View file

@ -1,23 +1,23 @@
import rss from '@astrojs/rss' import rss from '@astrojs/rss'
import { SITE } from '@consts' import { SITE } from '@consts'
import { getCollection } from 'astro:content'
import type { APIContext } from 'astro' import type { APIContext } from 'astro'
import { getCollection } from 'astro:content'
export async function GET(context: APIContext) { export async function GET(context: APIContext) {
try { try {
const blog = (await getCollection('blog')).filter( const blog = (await getCollection('blog')).filter(
(post) => !post.data.draft (post) => !post.data.draft,
) )
// Filter posts by tag 'rss-feed' // Filter posts by tag 'rss-feed'
const filteredBlogs = blog.filter( const filteredBlogs = blog.filter(
(post) => post.data.tags && post.data.tags.includes('rss-feed') (post) => post.data.tags && post.data.tags.includes('rss-feed'),
) )
// Sort posts by date // Sort posts by date
const items = [...filteredBlogs].sort( const items = [...filteredBlogs].sort(
(a, b) => (a, b) =>
new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf() new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf(),
) )
// Return RSS feed // Return RSS feed

View file

@ -36,7 +36,7 @@ export async function getStaticPaths() {
> >
<Container> <Container>
<div class="space-y-10"> <div class="space-y-10">
<div class="animate font-semibold text-black dark:text-white"> <div class="font-semibold text-black dark:text-white">
Tag: <span Tag: <span
class="mx-2 rounded-full bg-orange-300 px-3 py-2 transition-colors duration-300 ease-in-out hover:bg-cyan-200 dark:bg-orange-500 dark:hover:bg-cyan-500" class="mx-2 rounded-full bg-orange-300 px-3 py-2 transition-colors duration-300 ease-in-out hover:bg-cyan-200 dark:bg-orange-500 dark:hover:bg-cyan-500"
>#{tag}</span >#{tag}</span
@ -45,7 +45,7 @@ export async function getStaticPaths() {
<div class="space-y-4"> <div class="space-y-4">
{ {
posts.map((post) => ( posts.map((post) => (
<section class="animate space-y-4"> <section class="space-y-4">
<div> <div>
<ul class="not-prose flex flex-col gap-4"> <ul class="not-prose flex flex-col gap-4">
<li> <li>

View file

@ -14,7 +14,7 @@ const tags = blog
<Layout title="Tags" description="Tags"> <Layout title="Tags" description="Tags">
<Container> <Container>
<div class="space-y-10"> <div class="space-y-10">
<div class="animate font-semibold text-black dark:text-white">Tags</div> <div class="font-semibold text-black dark:text-white">Tags</div>
<ul class="flex flex-wrap"> <ul class="flex flex-wrap">
{ {
tags.map((tag) => ( tags.map((tag) => (

View file

@ -2,42 +2,6 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
html {
overflow-y: auto;
color-scheme: light;
scroll-padding-top: 100px;
}
html.dark {
color-scheme: dark;
}
html,
body {
@apply size-full;
}
body {
@apply font-sans antialiased;
@apply flex flex-col;
@apply bg-neutral-300 dark:bg-slate-900;
@apply text-black/75 dark:text-white/75;
}
header {
@apply fixed left-0 right-0 top-0 z-50 py-6;
@apply bg-neutral-100/75 dark:bg-neutral-900/75;
@apply saturate-200 backdrop-blur-sm;
}
main {
@apply flex-1 py-32;
}
footer {
@apply py-6 text-sm;
}
article { article {
@apply prose prose-neutral max-w-full dark:prose-invert prose-img:mx-auto prose-img:my-auto; @apply prose prose-neutral max-w-full dark:prose-invert prose-img:mx-auto prose-img:my-auto;
@apply prose-headings:font-semibold; @apply prose-headings:font-semibold;
@ -61,15 +25,6 @@ article {
scrollbar-color: #1e293b #0f172a; scrollbar-color: #1e293b #0f172a;
} }
.animate {
@apply -translate-y-3 opacity-0;
@apply transition-all duration-300 ease-out;
}
.animate.show {
@apply translate-y-0 opacity-100;
}
html #back-to-top { html #back-to-top {
@apply pointer-events-none opacity-0; @apply pointer-events-none opacity-0;
} }
@ -123,3 +78,65 @@ pre {
.copy-code:active { .copy-code:active {
@apply scale-90 transition-transform; @apply scale-90 transition-transform;
} }
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
}

View file

@ -2,7 +2,7 @@ import type { Config } from 'tailwindcss'
import defaultTheme from 'tailwindcss/defaultTheme' import defaultTheme from 'tailwindcss/defaultTheme'
const config: Config = { const config: Config = {
darkMode: 'class', darkMode: ['class', 'class'],
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: { theme: {
extend: { extend: {
@ -10,9 +10,56 @@ const config: Config = {
sans: ['Geist Sans', ...defaultTheme.fontFamily.sans], sans: ['Geist Sans', ...defaultTheme.fontFamily.sans],
mono: ['Geist Mono', ...defaultTheme.fontFamily.mono], mono: ['Geist Mono', ...defaultTheme.fontFamily.mono],
}, },
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))',
},
},
}, },
}, },
plugins: [require('@tailwindcss/typography')], plugins: [require('@tailwindcss/typography'), require('tailwindcss-animate')],
} }
export default config export default config

View file

@ -5,6 +5,8 @@
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@*": ["./src/*"] "@*": ["./src/*"]
} },
"jsx": "react-jsx",
"jsxImportSource": "react"
} }
} }