diff --git a/packages/mdx/package.json b/packages/mdx/package.json index 3672f544..67374266 100644 --- a/packages/mdx/package.json +++ b/packages/mdx/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "esbuild": "^0.21.4", - "mdx-bundler": "^10.0.2", + "mdx-bundler": "^10.0.3", "unified": "^11.0.5" }, "peerDependencies": { diff --git a/packages/mdx/src/react/client.tsx b/packages/mdx/src/react/client.tsx index 0a56d40b..24371002 100644 --- a/packages/mdx/src/react/client.tsx +++ b/packages/mdx/src/react/client.tsx @@ -1,4 +1,4 @@ -import { MDXContentProps, getMDXComponent } from "mdx-bundler/client"; +import { MDXContentProps, getMDXComponent } from "mdx-bundler/client/index.js"; import { useMemo } from "react"; export function useMDXComponent( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d47540bb..d08696e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -183,8 +183,8 @@ importers: specifier: ^0.21.4 version: 0.21.4 mdx-bundler: - specifier: ^10.0.2 - version: 10.0.2(esbuild@0.21.4) + specifier: ^10.0.3 + version: 10.0.3(esbuild@0.21.4) unified: specifier: ^11.0.5 version: 11.0.5 @@ -460,6 +460,46 @@ importers: specifier: ^5 version: 5.4.5 + samples/next-pages: + dependencies: + '@content-collections/core': + specifier: ^0.6.4 + version: link:../../packages/core + '@content-collections/mdx': + specifier: ^0.1.3 + version: link:../../packages/mdx + '@content-collections/next': + specifier: ^0.2.0 + version: link:../../packages/next + '@content-collections/sample-theme': + specifier: ^0.1.0 + version: link:../../utils/samplte-theme + next: + specifier: 14.2.7 + version: 14.2.7(@playwright/test@1.46.1)(react-dom@18.3.1)(react@18.3.1) + react: + specifier: ^18 + version: 18.3.1 + react-dom: + specifier: ^18 + version: 18.3.1(react@18.3.1) + devDependencies: + '@playwright/test': + specifier: ^1.46.1 + version: 1.46.1 + '@types/node': + specifier: ^20 + version: 20.14.9 + '@types/react': + specifier: ^18 + version: 18.3.3 + '@types/react-dom': + specifier: ^18 + version: 18.3.0 + typescript: + specifier: ^5 + version: 5.5.4 + samples/qwik: devDependencies: '@builder.io/qwik': @@ -785,6 +825,8 @@ importers: specifier: ^5.3.2 version: 5.3.3(@types/node@20.14.9) + utils/samplte-theme: {} + website: dependencies: '@fumadocs/content-collections': @@ -1237,25 +1279,20 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 + dev: true /@babel/runtime@7.24.7: resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 - - /@babel/runtime@7.25.4: - resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 + dev: true /@babel/runtime@7.25.6: resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 - dev: true /@babel/template@7.24.0: resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} @@ -1835,8 +1872,8 @@ packages: peerDependencies: esbuild: '*' dependencies: - '@types/resolve': 1.20.6 - debug: 4.3.4 + '@types/resolve': 1.20.2 + debug: 4.3.6 esbuild: 0.21.4 escape-string-regexp: 4.0.0 resolve: 1.22.8 @@ -3175,7 +3212,7 @@ packages: /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -3794,7 +3831,7 @@ packages: /@radix-ui/primitive@1.0.1: resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 dev: false /@radix-ui/primitive@1.1.0: @@ -3908,7 +3945,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@types/react': 18.3.3 react: 18.3.1 dev: false @@ -3935,7 +3972,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@types/react': 18.3.3 react: 18.3.1 dev: false @@ -3966,7 +4003,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -4046,7 +4083,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) @@ -4091,7 +4128,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@types/react': 18.3.3 react: 18.3.1 dev: false @@ -4122,7 +4159,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -4163,7 +4200,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@types/react': 18.3.3 react: 18.3.1 @@ -4259,7 +4296,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) '@types/react': 18.3.3 '@types/react-dom': 18.3.0 @@ -4301,7 +4338,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@types/react': 18.3.3 @@ -4344,7 +4381,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1) '@types/react': 18.3.3 '@types/react-dom': 18.3.0 @@ -4506,7 +4543,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@types/react': 18.3.3 react: 18.3.1 @@ -4562,7 +4599,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@types/react': 18.3.3 react: 18.3.1 dev: false @@ -4589,7 +4626,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@types/react': 18.3.3 react: 18.3.1 @@ -4618,7 +4655,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@types/react': 18.3.3 react: 18.3.1 @@ -4647,7 +4684,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@types/react': 18.3.3 react: 18.3.1 dev: false @@ -5741,7 +5778,7 @@ packages: resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} dependencies: '@swc/counter': 0.1.3 - tslib: 2.6.3 + tslib: 2.7.0 /@tailwindcss/typography@0.5.14(tailwindcss@3.4.4): resolution: {integrity: sha512-ZvOCjUbsJBjL9CxQBn+VEnFpouzuKhxh2dH8xMIWHILL+HfOYtlAkWcyoon8LlzE53d2Yo6YO6pahKKNW3q1YQ==} @@ -5753,6 +5790,19 @@ packages: lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 tailwindcss: 3.4.4 + dev: true + + /@tailwindcss/typography@0.5.15(tailwindcss@3.4.4): + resolution: {integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20' + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.4 + dev: false /@testing-library/dom@10.4.0: resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} @@ -5850,7 +5900,7 @@ packages: /@types/conventional-commits-parser@5.0.0: resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} dependencies: - '@types/node': 22.5.2 + '@types/node': 20.14.9 dev: true /@types/cookie@0.6.0: @@ -5890,7 +5940,7 @@ packages: /@types/http-proxy@1.17.14: resolution: {integrity: sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==} dependencies: - '@types/node': 22.5.2 + '@types/node': 20.14.9 dev: false /@types/json-schema@7.0.15: @@ -5954,11 +6004,6 @@ packages: dependencies: undici-types: 5.26.5 - /@types/node@22.5.2: - resolution: {integrity: sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==} - dependencies: - undici-types: 6.19.8 - /@types/normalize-package-data@2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: true @@ -5993,10 +6038,6 @@ packages: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} dev: false - /@types/resolve@1.20.6: - resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} - dev: false - /@types/semver@7.5.8: resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true @@ -7743,7 +7784,7 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.6 dev: true /dax-sh@0.39.2: @@ -7967,7 +8008,7 @@ packages: resolution: {integrity: sha512-PSy0zQwMg5O+LjT5Mz7vnKC8I7DfWLPF6M7oepqW7WP5mn2CY3hz46xZOa1GJY+KVfyXhdmz6+tdgXwrHlZc5g==} engines: {node: ^16.14.0 || >=18.12.0} dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.6 fastest-levenshtein: 1.0.16 lodash.deburr: 4.1.0 dev: false @@ -8886,7 +8927,7 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} dependencies: - '@types/node': 22.5.2 + '@types/node': 20.14.9 require-like: 0.1.2 dev: true @@ -9367,7 +9408,7 @@ packages: '@radix-ui/react-popover': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) '@radix-ui/react-scroll-area': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) '@radix-ui/react-tabs': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@tailwindcss/typography': 0.5.14(tailwindcss@3.4.4) + '@tailwindcss/typography': 0.5.15(tailwindcss@3.4.4) class-variance-authority: 0.7.0 cmdk: 1.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) fumadocs-core: 13.3.2(@types/react@18.3.3)(next@14.2.7)(react-dom@18.3.1)(react@18.3.1) @@ -10595,10 +10636,6 @@ packages: resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} dev: true - /jiti@1.21.0: - resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} - hasBin: true - /jiti@1.21.6: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true @@ -11379,13 +11416,13 @@ packages: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} dev: true - /mdx-bundler@10.0.2(esbuild@0.21.4): - resolution: {integrity: sha512-0wF0zoCv+Ms4G+eSlk/jaKYoJHc0oXBaOma3kYlFJiKq9H8h41Dd66ioDBGF4noy80Pf7KTBQlyHfEpTqVml7A==} + /mdx-bundler@10.0.3(esbuild@0.21.4): + resolution: {integrity: sha512-vRtVZ5t+nUP0QtoRVgjDFO10YDjRgKe/19ie0IR8FqE8SugNn5RP4sCWBPzKoEwoGbqfQOrgHy+PHCVyfaCDQQ==} engines: {node: '>=18', npm: '>=6'} peerDependencies: esbuild: 0.* dependencies: - '@babel/runtime': 7.24.5 + '@babel/runtime': 7.25.6 '@esbuild-plugins/node-resolve': 0.2.2(esbuild@0.21.4) '@fal-works/esbuild-plugin-global-externals': 2.1.2 '@mdx-js/esbuild': 3.0.1(esbuild@0.21.4) @@ -11402,7 +11439,7 @@ packages: /media-query-parser@2.0.2: resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 dev: true /media-typer@0.3.0: @@ -12960,6 +12997,7 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true /picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} @@ -13038,41 +13076,25 @@ packages: postcss: 8.4.41 dev: true - /postcss-import@15.1.0(postcss@8.4.39): + /postcss-import@15.1.0(postcss@8.4.41): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.4.39 + postcss: 8.4.41 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 - /postcss-js@4.0.1(postcss@8.4.39): + /postcss-js@4.0.1(postcss@8.4.41): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 - postcss: 8.4.39 - - /postcss-load-config@4.0.2(postcss@8.4.39): - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - dependencies: - lilconfig: 3.1.1 - postcss: 8.4.39 - yaml: 2.4.5 + postcss: 8.4.41 /postcss-load-config@4.0.2(postcss@8.4.41): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} @@ -13089,7 +13111,6 @@ packages: lilconfig: 3.1.1 postcss: 8.4.41 yaml: 2.4.5 - dev: true /postcss-modules-extract-imports@3.1.0(postcss@8.4.41): resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} @@ -13148,13 +13169,13 @@ packages: string-hash: 1.1.3 dev: true - /postcss-nested@6.0.1(postcss@8.4.39): + /postcss-nested@6.0.1(postcss@8.4.41): resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.39 + postcss: 8.4.41 postcss-selector-parser: 6.0.16 /postcss-selector-parser@6.0.10: @@ -13189,6 +13210,7 @@ packages: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 + dev: true /postcss@8.4.41: resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} @@ -14902,17 +14924,17 @@ packages: fast-glob: 3.3.2 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.21.0 + jiti: 1.21.6 lilconfig: 2.1.0 micromatch: 4.0.8 normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.1 - postcss: 8.4.39 - postcss-import: 15.1.0(postcss@8.4.39) - postcss-js: 4.0.1(postcss@8.4.39) - postcss-load-config: 4.0.2(postcss@8.4.39) - postcss-nested: 6.0.1(postcss@8.4.39) + postcss: 8.4.41 + postcss-import: 15.1.0(postcss@8.4.41) + postcss-js: 4.0.1(postcss@8.4.41) + postcss-load-config: 4.0.2(postcss@8.4.41) + postcss-nested: 6.0.1(postcss@8.4.41) postcss-selector-parser: 6.0.16 resolve: 1.22.8 sucrase: 3.35.0 @@ -15211,7 +15233,7 @@ packages: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(postcss@8.4.39) + postcss-load-config: 4.0.2(postcss@8.4.41) resolve-from: 5.0.0 rollup: 4.17.2 source-map: 0.8.0-beta.0 @@ -15462,9 +15484,6 @@ packages: resolution: {integrity: sha512-3OeMF5Lyowe8VW0skf5qaIE7Or3yS9LS7fvMUI0gg4YxpIBVg0L8BxCmROw2CcYhSkpR68Epz7CGc8MPj94Uww==} dev: false - /undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - /undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} engines: {node: '>=14.0'} @@ -15751,7 +15770,7 @@ packages: dependencies: browserslist: 4.23.0 escalade: 3.2.0 - picocolors: 1.0.0 + picocolors: 1.0.1 /uqr@0.1.2: resolution: {integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6c8fd766..87b3f65b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ packages: - "packages/*" +- "utils/*" - "samples/*" - "website" diff --git a/samples/next-pages/.gitignore b/samples/next-pages/.gitignore new file mode 100644 index 00000000..2d9a0986 --- /dev/null +++ b/samples/next-pages/.gitignore @@ -0,0 +1,39 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# content collections +.content-collections diff --git a/samples/next-pages/README.md b/samples/next-pages/README.md new file mode 100644 index 00000000..0bfddf02 --- /dev/null +++ b/samples/next-pages/README.md @@ -0,0 +1,16 @@ +--- +title: Next.js Pages +description: Using content-collections with Next.js Pages Router +tags: + - next.js + - react + - mdx +adapter: next +--- + +Use the `@content-collections/next` package to integrate Content Collections into your Next.js project. + +For more information, refer to the following resources: + +- [Next.js Quick Start](https://www.content-collections.dev/docs/quickstart/next) +- [Next.js adapter documentation](https://www.content-collections.dev/docs/adapter/next/) diff --git a/samples/next-pages/content-collections.ts b/samples/next-pages/content-collections.ts new file mode 100644 index 00000000..543d231f --- /dev/null +++ b/samples/next-pages/content-collections.ts @@ -0,0 +1,25 @@ +import { defineCollection, defineConfig } from "@content-collections/core"; +import { compileMDX } from "@content-collections/mdx"; + +const posts = defineCollection({ + name: "posts", + directory: "src/content/posts", + include: "*.mdx", + schema: (z) => ({ + title: z.string(), + summary: z.string(), + date: z.string(), + author: z.string(), + }), + transform: async (post, ctx) => { + const code = await compileMDX(ctx, post) + return { + ...post, + code + } + } +}); + +export default defineConfig({ + collections: [posts], +}); \ No newline at end of file diff --git a/samples/next-pages/next.config.mjs b/samples/next-pages/next.config.mjs new file mode 100644 index 00000000..e94b9f06 --- /dev/null +++ b/samples/next-pages/next.config.mjs @@ -0,0 +1,7 @@ +import { withContentCollections } from "@content-collections/next"; + +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default withContentCollections(nextConfig); + diff --git a/samples/next-pages/package.json b/samples/next-pages/package.json new file mode 100644 index 00000000..13f36391 --- /dev/null +++ b/samples/next-pages/package.json @@ -0,0 +1,26 @@ +{ + "name": "samples-next-pages", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "lint": "next lint", + "test": "playwright test" + }, + "dependencies": { + "@content-collections/core": "^0.6.4", + "@content-collections/mdx": "^0.1.3", + "@content-collections/next": "^0.2.0", + "@content-collections/sample-theme": "^0.1.0", + "next": "14.2.7", + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@playwright/test": "^1.46.1", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "typescript": "^5" + } +} diff --git a/samples/next-pages/playwright.config.ts b/samples/next-pages/playwright.config.ts new file mode 100644 index 00000000..7ebefde9 --- /dev/null +++ b/samples/next-pages/playwright.config.ts @@ -0,0 +1,47 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://localhost:3002', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /** one browser is enough to test if out collections work */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'pnpm run dev --port=3002', + url: 'http://localhost:3002', + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/samples/next-pages/src/components/Metadata.tsx b/samples/next-pages/src/components/Metadata.tsx new file mode 100644 index 00000000..3198aba6 --- /dev/null +++ b/samples/next-pages/src/components/Metadata.tsx @@ -0,0 +1,29 @@ +import Head from "next/head"; + +type Props = { + title?: string; + description?: string; +}; + +const defaultTitle = "ContentCrafter Inc."; +const titleSuffix = " | " + defaultTitle; + +const defaultDescription = + "From Worldly Wonders to Polished Perfection - Crafting Content That Captivates and Converts"; + +export default function Metadata({ title, description }: Props) { + return ( + + {title ? title + titleSuffix : defaultTitle} + + + + + ); +} diff --git a/samples/next-pages/src/content/posts/llama-photobomb.mdx b/samples/next-pages/src/content/posts/llama-photobomb.mdx new file mode 100644 index 00000000..7950151e --- /dev/null +++ b/samples/next-pages/src/content/posts/llama-photobomb.mdx @@ -0,0 +1,38 @@ +--- +title: "When Llamas Photobomb Your Selfie" +summary: "Dive into the whimsical world of ContentCrafter Inc., where our adventurous Collectors, meticulous Validators, and creative Transformers work together to create captivating content. From llama photobombs to piano-playing cats, discover the quirky and humorous experiences that make our team’s journey as entertaining as the content we produce." +date: 2024-09-04 +author: Jenna Thompson +--- + +Ever wondered what it’s like to have your selfie photobombed by a llama or to validate a story about a talking parrot? Welcome to the whimsical world of ContentCrafter Inc., where every day is an adventure, and every piece of content has a story behind it. Our team of Collectors, Validators, and Transformers work tirelessly (and often hilariously) to bring you the most unique and captivating content. Buckle up for a ride through the quirky experiences of our team! + +## Llama Drama and Other Collector Tales + +Imagine trekking through the Andes, camera in hand, only to have your perfect shot ruined by a curious llama. That’s just a regular Tuesday for our Collectors. These adventurous souls travel the globe, from the bustling markets of Marrakech to the serene temples of Kyoto, in search of content that’s as unique as it is engaging. + +Take Jenna, one of our star Collectors, who recently found herself in a small village in Peru. She was documenting a traditional festival when a llama decided it wanted to be the star of the show. The resulting photos were a hit, capturing the playful spirit of the festival in a way no one expected. + +But it’s not all fun and llamas. Our Collectors often face unexpected challenges. Like the time Mark had to navigate a maze of alleyways in Venice to find a hidden art installation, only to discover it had been moved the day before. Or when Sarah’s drone got stuck in a tree in the Amazon rainforest, and she had to enlist the help of a local tribe to retrieve it. + +Despite the mishaps, our Collectors always manage to find the most fascinating stories and visuals. Whether it’s a street performer in Paris or a hidden beach in Thailand, they bring back content that’s sure to captivate our audience. + +## Validators: The Unsung Heroes + +Once the content is collected, it’s up to our Validators to ensure it’s up to snuff. These detail-oriented professionals are the gatekeepers of quality, meticulously checking every piece of content for accuracy and authenticity. + +Validators often find themselves in amusing situations. Like the time Emily had to verify a story about a cat that could play the piano. After hours of research and a few skeptical phone calls, she finally confirmed it was true – and even got a video to prove it! + +The Validators’ office is a hub of activity, filled with the sounds of keyboards clicking and the occasional burst of laughter. They often share the oddest pieces of content they come across, like the story of a man who claimed to have built a working submarine in his backyard. The Validators’ job might be serious, but they know how to find humor in the most unexpected places. + +## Transformers: Turning Chaos into Content + +After the Validators give the green light, it’s time for the Transformers to work their magic. These creative wizards take the raw content and shape it into polished pieces that meet our clients’ needs. + +The Transformers’ brainstorming sessions are legendary. Ideas fly around the room, some more outlandish than others. Like the time they considered turning a series of photos from a cheese festival into a noir-style detective story. Or when they debated whether a video of a dog skateboarding could be turned into a motivational speech. + +Eventually, the team settles on the perfect approach, transforming the content into something truly special. The final product is always a testament to their creativity and dedication, whether it’s a captivating blog post, an engaging social media campaign, or a stunning visual story. + +## Conclusion + +At ContentCrafter Inc., every piece of content has a story behind it – and often, that story is as entertaining as the content itself. From the adventurous Collectors to the meticulous Validators and the creative Transformers, our team works together to craft content that captivates and converts. It’s a wild ride, but we wouldn’t have it any other way. So next time you see a piece of content from ContentCrafter Inc., remember: there’s a good chance a llama was involved. \ No newline at end of file diff --git a/samples/next-pages/src/content/posts/yak-stole-my-notebook.mdx b/samples/next-pages/src/content/posts/yak-stole-my-notebook.mdx new file mode 100644 index 00000000..6e51f07b --- /dev/null +++ b/samples/next-pages/src/content/posts/yak-stole-my-notebook.mdx @@ -0,0 +1,54 @@ +--- +title: "When a Yak Stole My Notebook" +summary: "Join the team at ContentCrafter Inc. as they navigate the quirky and humorous challenges of content creation. From yaks stealing notebooks to heated debates over llamas, our Collectors, Validators, and Transformers bring you unique and captivating stories from around the world. Discover the joy and laughter behind every piece of content we craft." +date: 2024-09-04 +author: Jane Doe +--- + +At ContentCrafter Inc., every day is an adventure. From the bustling streets of Tokyo to the serene landscapes of Patagonia, our team of Collectors, Validators, and Transformers work tirelessly to bring you the most unique and captivating content. But it's not all smooth sailing—sometimes, a yak steals your notebook, and that's just the beginning of the fun. + +## The Collectors' Adventure + +### Yak Attack and Other Tales + +Imagine trekking through the Himalayas, the air crisp and the scenery breathtaking. Our Collectors, armed with cameras and notebooks, were on a mission to capture the essence of local culture. Just as they were about to interview a local artisan, a mischievous yak decided that one of the notebooks looked like a tasty snack. Chaos ensued as our brave Collector chased the yak, much to the amusement of the locals. Eventually, the notebook was retrieved, albeit a bit slobbery, and the interview continued. + +### Lost in Translation + +In the bustling markets of Marrakech, another Collector found herself in a hilarious predicament. Trying to communicate with a vendor who spoke only Arabic, she accidentally agreed to buy 50 kilos of dates instead of just a sample. The vendor's laughter echoed through the market as they sorted out the misunderstanding, and our Collector left with a story to tell and a much lighter wallet. + +### Unexpected Discoveries + +From ancient ruins in Greece to hidden cafes in Paris, our Collectors stumble upon the most fascinating content. One Collector, while exploring the Amazon rainforest, discovered a tribe that had never been documented before. The tribe's unique customs and traditions became a centerpiece of our content, showcasing the incredible diversity of human culture. + +## The Validators' Hurdles + +### The Great Llama Debate + +Back at headquarters, our Validators faced their own set of challenges. One day, they received a photo of what was claimed to be a rare Andean llama. However, a heated debate erupted among the Validators—was it really a llama, or just a particularly fluffy alpaca? After hours of research and a few phone calls to local experts, the mystery was solved, and the Validators could finally move on. + +### The Case of the Mysterious Manuscript + +Another Validator found herself poring over an ancient manuscript that had been discovered in a remote monastery. The manuscript was written in a language no one could identify. After weeks of painstaking research and a few sleepless nights, she cracked the code, revealing a treasure trove of historical information that added immense value to our content. + +### Funny Interactions + +Validators are known for their meticulous nature, and their interactions often reflect this. One Validator, while examining a photo of a rare bird, exclaimed, "This feather pattern is all wrong! It's like putting polka dots on a zebra!" The team burst into laughter, and the Validator's keen eye saved the day once again. + +## The Transformers' Magic + +### Brainstorming Shenanigans + +Once the content is validated, it's time for the Transformers to work their magic. Brainstorming sessions are a whirlwind of creativity and hilarity. One Transformer suggested turning a simple travel story into a musical, complete with singing camels and dancing palm trees. While the idea was a bit too outlandish, it sparked a creative flow that led to a beautifully crafted narrative. + +### The Perfect Solution + +Transformers often face the challenge of making dry facts engaging. One Transformer, tasked with creating content about ancient pottery, decided to write it from the perspective of the pottery itself. The result was a charming and informative piece that captivated readers and brought history to life. + +### The Final Touch + +After countless revisions and a few more laughs, the content is polished to perfection. The team gathers to review the final product, and there's a sense of satisfaction and pride in what they've created. The journey from raw content to finished piece is always an adventure, filled with unexpected twists and turns. + +## Conclusion + +At ContentCrafter Inc., every piece of content is a labor of love. From the Collectors' daring escapades to the Validators' meticulous scrutiny and the Transformers' creative flair, our team works together to craft stories that captivate and convert. And while there are plenty of laughs along the way, the joy of creating something truly unique and valuable is what drives us forward. So, the next time you read one of our stories, remember the yak, the llama debate, and the singing camels—because at ContentCrafter Inc., the journey is just as important as the destination. \ No newline at end of file diff --git a/samples/next-pages/src/pages/_app.tsx b/samples/next-pages/src/pages/_app.tsx new file mode 100644 index 00000000..1add9ffe --- /dev/null +++ b/samples/next-pages/src/pages/_app.tsx @@ -0,0 +1,6 @@ +import type { AppProps } from "next/app"; +import "@content-collections/sample-theme/sample.css"; + +export default function MyApp({ Component, pageProps }: AppProps) { + return ; +} diff --git a/samples/next-pages/src/pages/_document.tsx b/samples/next-pages/src/pages/_document.tsx new file mode 100644 index 00000000..775275c8 --- /dev/null +++ b/samples/next-pages/src/pages/_document.tsx @@ -0,0 +1,30 @@ +import Link from "next/link"; +import { Html, Head, Main, NextScript } from "next/document"; + +export default function Document() { + return ( + + + +
+

ContentCrafter Inc.

+

+ From Worldly Wonders to Polished Perfection – Crafting Content That + Captivates and Converts. +

+ +
+
+
+
+
+ ↑ Back to top +

Copyright © {new Date().getFullYear()} ContentCrafter Inc.

+
+ + + + ); +} diff --git a/samples/next-pages/src/pages/index.tsx b/samples/next-pages/src/pages/index.tsx new file mode 100644 index 00000000..054d2a01 --- /dev/null +++ b/samples/next-pages/src/pages/index.tsx @@ -0,0 +1,39 @@ +import { allPosts } from "content-collections"; +import Link from "next/link"; +import Metadata from "../components/Metadata"; +import { InferGetStaticPropsType } from "next"; + +type Props = InferGetStaticPropsType; + +export default function Home({ posts }: Props) { + return ( + <> + +

Posts

+
+ {posts.map((post) => ( + +
+

{post.title}

+ +
+

{post.summary}

+ + ))} +
+ + ); +} + +export function getStaticProps() { + return { + props: { + posts: allPosts.map((post) => ({ + title: post.title, + summary: post.summary, + date: post.date, + href: `/posts/${post._meta.path}`, + })), + }, + }; +} diff --git a/samples/next-pages/src/pages/posts/[slug].tsx b/samples/next-pages/src/pages/posts/[slug].tsx new file mode 100644 index 00000000..7f3736b8 --- /dev/null +++ b/samples/next-pages/src/pages/posts/[slug].tsx @@ -0,0 +1,52 @@ +import { allPosts } from "content-collections"; +import { MDXContent } from "@content-collections/mdx/react"; +import { GetServerSidePropsContext, InferGetStaticPropsType } from "next"; +import Metadata from "@/src/components/Metadata"; + +type Props = InferGetStaticPropsType; + +export default function Post({ title, summary, code, author, date }: Props) { + return ( +
+ +
+

{title}

+
+
+ +
+
+

By {author}

+ +
+
+ ); +} + +type Params = { + slug: string; +}; + +export function getStaticPaths() { + return { + paths: allPosts.map((post) => ({ params: { slug: post._meta.path } })), + fallback: false, + }; +} + +export function getStaticProps({ params }: GetServerSidePropsContext) { + const post = allPosts.find((post) => post._meta.path === params?.slug); + if (!post) { + return { notFound: true }; + } + + return { + props: { + title: post.title, + summary: post.summary, + code: post.code, + author: post.author, + date: post.date, + }, + }; +} diff --git a/samples/next-pages/tests/collection.spec.ts b/samples/next-pages/tests/collection.spec.ts new file mode 100644 index 00000000..f6263cd8 --- /dev/null +++ b/samples/next-pages/tests/collection.spec.ts @@ -0,0 +1,32 @@ +import { test, expect } from "@playwright/test"; + +test("should render posts overview", async ({ page }) => { + await page.goto("/"); + await expect( + page.getByRole("heading", { name: "When Llamas Photobomb Your Selfie" }) + ).toBeVisible(); + await expect( + page.getByRole("heading", { name: "When a Yak Stole My Notebook" }) + ).toBeVisible(); +}); + +test("should navigate to post", async ({ page }) => { + await page.goto("/"); + + const heading = await page.getByRole("heading", { name: "When Llamas Photobomb Your Selfie" }); + await heading.click(); + + await expect(page).toHaveURL(new RegExp("/posts/llama-photobomb$")); +}); + +test("should render post", async ({ page }) => { + await page.goto("/posts/yak-stole-my-notebook"); + + await expect( + page.getByRole("heading", { name: "When a Yak Stole My Notebook" }) + ).toBeVisible(); + + await expect( + page.getByRole("heading", { name: "The Collectors' Adventure" }) + ).toBeVisible(); +}); diff --git a/samples/next-pages/tsconfig.json b/samples/next-pages/tsconfig.json new file mode 100644 index 00000000..310b71c5 --- /dev/null +++ b/samples/next-pages/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"], + "content-collections": ["./.content-collections/generated"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/samples/next/README.md b/samples/next/README.md index 56bae3ba..1fd1de6f 100644 --- a/samples/next/README.md +++ b/samples/next/README.md @@ -1,9 +1,10 @@ --- title: Next.js -description: Using the content-collections next adapter +description: Using content-collections with Next.js App Router tags: - next.js - react + - rsc adapter: next --- diff --git a/utils/samplte-theme/package.json b/utils/samplte-theme/package.json new file mode 100644 index 00000000..ce8f6cf0 --- /dev/null +++ b/utils/samplte-theme/package.json @@ -0,0 +1,8 @@ +{ + "name": "@content-collections/sample-theme", + "description": "A theme for Content Collections Samples", + "version": "0.1.0", + "files": [ + "sample.css" + ] +} diff --git a/utils/samplte-theme/sample.css b/utils/samplte-theme/sample.css new file mode 100644 index 00000000..5ad23f1f --- /dev/null +++ b/utils/samplte-theme/sample.css @@ -0,0 +1,230 @@ +/* + Josh's Custom CSS Reset + https://www.joshwcomeau.com/css/custom-css-reset/ +*/ + +*, +*::before, +*::after { + box-sizing: border-box; +} + +* { + margin: 0; +} + +body { + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; +} + +input, +button, +textarea, +select { + font: inherit; +} + +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} + +#root, +#__next { + isolation: isolate; +} + +/** more resets */ + +a { + color: inherit; + text-decoration: none; +} + +/** design tokens */ + +:root { + color-scheme: light dark; + + --font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + + --color-background: light-dark(#f9fafb, #111827); + --color-border: light-dark(#d1d5db, #374151); + --color-text-muted: light-dark(#4b5563, #9ca3af); + --color-text: light-dark(#374151, #d1d5db); + --color-heading: light-dark(#111827, #f9fafb); + --color-link: light-dark(#111827, #f9fafb); + + --color-background-accent: light-dark(#f0fdfa, #042f2e); + --color-primary: #14b8a6; +} + +/** main layout */ + +html, body { + background-color: var(--color-background); + scroll-behavior: smooth; +} + +body { + display: grid; + grid-template-rows: auto 1fr auto; + min-height: 100vh; + padding: 2.5rem; + color: var(--color-text); + font-family: var(--font); +} + +nav { + margin-top: 1rem; +} + +main { + margin-top: 1rem; +} + +h1 { + font-size: 3rem; + line-height: 1; + font-weight: 700; + color: var(--color-heading); +} + +h2 { + font-size: 1.5rem; + line-height: 2rem; + font-weight: 600; + color: var(--color-heading); +} + +h3 { + color: var(--color-heading); +} + +body > header { + h1 { + margin-bottom: 0.5rem; + } + + p { + color: var(--color-text); + } +} + +body > footer { + margin-top: 2.5rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +nav a, body > footer a { + color: var(--color-link); + font-weight: 600; + text-decoration: underline; + text-underline-offset: 2px; + text-decoration-thickness: 2px; + + &:hover { + text-decoration-color: var(--color-primary); + } +} + +/** overview **/ + +.posts { + margin-top: 1rem; + max-width: 65ch; + display: flex; + flex-direction: column; + gap: 1rem; + + a { + border: 2px solid var(--color-border); + padding: 1rem; + border-radius: 0.5rem; + box-shadow: + 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); + + &:hover { + background-color: var(--color-background-accent); + border-color: var(--color-primary); + } + + header { + display: flex; + justify-content: space-between; + margin-bottom: 0.5rem; + + h3 { + font-size: 1.25rem; + line-height: 1.75rem; + font-weight: 600; + } + + time { + font-size: 0.875rem; + color: var(--color-text-muted); + flex-shrink: 0; + } + } + } +} + +/** detail **/ + +.post { + margin-top: 1rem; + max-width: 65ch; + + header h2 { + font-size: 1.875rem; + line-height: 2.25rem; + font-weight: 700; + } + + footer { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.875rem; + line-height: 1.25rem; + color: var(--color-text-muted); + } +} + +.content { + font-size: 1rem; + line-height: 1.75; + + p { + margin-top: 1.25em; + margin-bottom: 1.25em; + } + + h2 { + font-weight: 700; + font-size: 1.5em; + margin-top: 2em; + margin-bottom: 1em; + line-height: 1.3333333; + } +}