From 3e60b0ebb79d112127cd8916416e2b5cd9e390a4 Mon Sep 17 00:00:00 2001 From: eryue0220 Date: Thu, 27 Aug 2026 00:02:52 +0800 Subject: [PATCH 1/2] add new example case --- astro.config.mjs | 11 + package.json | 7 +- pnpm-lock.yaml | 304 ++++++++++++++++++++++++++ src/components/CodeSample.astro | 26 +++ src/components/CssPlayground.astro | 62 ++++++ src/components/PlaygroundCta.astro | 12 + src/components/SiteNav.astro | 17 +- src/env.d.ts | 13 ++ src/layouts/ExamplesLayout.astro | 139 ++++++++++++ src/layouts/PlaygroundLayout.astro | 64 ++++++ src/lib/examples.ts | 41 ++++ src/lib/monaco-env.ts | 17 ++ src/lib/playground-app.ts | 226 +++++++++++++++++++ src/lib/playground-process.ts | 41 ++++ src/lib/playgrounds.ts | 276 +++++++++++++++++++++++ src/pages/examples/astro.astro | 64 ++++++ src/pages/examples/autoprefixer.astro | 21 ++ src/pages/examples/cssnano.astro | 31 +++ src/pages/examples/default.astro | 110 ++++++++++ src/pages/examples/import.astro | 30 +++ src/pages/examples/index.astro | 3 + src/pages/examples/nested.astro | 36 +++ src/pages/examples/nextjs.astro | 56 +++++ src/pages/examples/nuxt.astro | 56 +++++ src/pages/examples/sveltekit.astro | 66 ++++++ src/pages/examples/tailwindcss.astro | 89 ++++++++ src/pages/examples/vite.astro | 49 +++++ src/pages/examples/vue.astro | 60 +++++ src/pages/examples/webpack.astro | 63 ++++++ src/pages/guide/get-started.md | 2 + src/pages/index.astro | 7 +- src/pages/playground/[id].astro | 24 ++ src/pages/playground/index.astro | 3 + src/styles/global.css | 227 +++++++++++++++++++ 34 files changed, 2246 insertions(+), 7 deletions(-) create mode 100644 src/components/CodeSample.astro create mode 100644 src/components/CssPlayground.astro create mode 100644 src/components/PlaygroundCta.astro create mode 100644 src/env.d.ts create mode 100644 src/layouts/ExamplesLayout.astro create mode 100644 src/layouts/PlaygroundLayout.astro create mode 100644 src/lib/examples.ts create mode 100644 src/lib/monaco-env.ts create mode 100644 src/lib/playground-app.ts create mode 100644 src/lib/playground-process.ts create mode 100644 src/lib/playgrounds.ts create mode 100644 src/pages/examples/astro.astro create mode 100644 src/pages/examples/autoprefixer.astro create mode 100644 src/pages/examples/cssnano.astro create mode 100644 src/pages/examples/default.astro create mode 100644 src/pages/examples/import.astro create mode 100644 src/pages/examples/index.astro create mode 100644 src/pages/examples/nested.astro create mode 100644 src/pages/examples/nextjs.astro create mode 100644 src/pages/examples/nuxt.astro create mode 100644 src/pages/examples/sveltekit.astro create mode 100644 src/pages/examples/tailwindcss.astro create mode 100644 src/pages/examples/vite.astro create mode 100644 src/pages/examples/vue.astro create mode 100644 src/pages/examples/webpack.astro create mode 100644 src/pages/playground/[id].astro create mode 100644 src/pages/playground/index.astro diff --git a/astro.config.mjs b/astro.config.mjs index 1595b47..940ceb1 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -7,5 +7,16 @@ export default defineConfig({ base: '/', vite: { plugins: [tailwindcss()], + worker: { + format: 'es', + }, + assetsInclude: ['**/*.wasm'], + optimizeDeps: { + exclude: ['@postcss-go/core'], + }, + build: { + // Keep the classic WASM worker as a file. A data: URL cannot importScripts wasm_exec.js. + assetsInlineLimit: 0, + }, }, }); diff --git a/package.json b/package.json index af2605e..c8c16b5 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,13 @@ "*.{json,md,yml,yaml,css}": "prettier --write" }, "dependencies": { + "@postcss-go/core": "^0.0.1", "@tailwindcss/vite": "^4.3.3", - "astro": "^7.1.3" + "astro": "^7.1.3", + "autoprefixer": "^10.5.4", + "monaco-editor": "^0.56.0", + "postcss": "^8.5.26", + "postcss-nested": "^8.0.1" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43855f7..76fe917 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,27 @@ importers: .: dependencies: + '@postcss-go/core': + specifier: ^0.0.1 + version: 0.0.1 '@tailwindcss/vite': specifier: ^4.3.3 version: 4.3.3(vite@8.2.1(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)) astro: specifier: ^7.1.3 version: 7.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(jiti@2.7.0)(yaml@2.9.0) + autoprefixer: + specifier: ^10.5.4 + version: 10.5.4(postcss@8.5.26) + monaco-editor: + specifier: ^0.56.0 + version: 0.56.0 + postcss: + specifier: ^8.5.26 + version: 8.5.26 + postcss-nested: + specifier: ^8.0.1 + version: 8.0.1(postcss@8.5.26) devDependencies: '@eslint/js': specifier: ^10.0.1 @@ -666,6 +681,41 @@ packages: '@oxc-project/types@0.144.0': resolution: {integrity: sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==} + '@postcss-go/core@0.0.1': + resolution: {integrity: sha512-ruprOBNp8yo9DHaZmf1d7+qMKr6Ofp64JPliWawLGZGn0KQrSs8r5Ovb4NqWm5zc97UayAKsppGDWQ/C/RDp5g==} + engines: {node: '>=18'} + hasBin: true + + '@postcss-go/native-darwin-arm64@0.0.1': + resolution: {integrity: sha512-OQALbux4tEr1n7dRPKumQ4QfBNmK/kEWUbRLRJaRZXy9x1ezBCJ0Yj8ZACzE5KG0pFgKKmNzsBPDUjC49lgKmw==} + cpu: [arm64] + os: [darwin] + + '@postcss-go/native-darwin-x64@0.0.1': + resolution: {integrity: sha512-QMp0FvJ48FFPLXaou8Ngp5h3HrINN+VRBHFR9quFOC+tKEreSR7vylsXRq1Sg1oDR040bZv7mA9+CWp3i3nhlQ==} + cpu: [x64] + os: [darwin] + + '@postcss-go/native-linux-arm64-gnu@0.0.1': + resolution: {integrity: sha512-QgaLCSVcBNe/Z/KA6X94vCsvApF+d9v8yNJFcINy1uqZ3jcN6XxKL9GbVhfYSeGjBzfx1rMTsv51l8VHqrxuvg==} + cpu: [arm64] + os: [linux] + + '@postcss-go/native-linux-x64-gnu@0.0.1': + resolution: {integrity: sha512-aadqh6WjOgzyy+UA7g/H8NbImS168r/Xr11TAuzQcgMb3OO72UzN/HdkMt4HcsrbQKMOsgI8YKjlcORN3wdS0g==} + cpu: [x64] + os: [linux] + + '@postcss-go/native-win32-arm64-msvc@0.0.1': + resolution: {integrity: sha512-BjGwIy8BgEKC6Lhp0/IVPOWa/r6ccnM6hTaD1dJeA6tLx7xm7W/gDkEOaKohFqCMXF/lNAW+B5uXPgg5W/dQkQ==} + cpu: [arm64] + os: [win32] + + '@postcss-go/native-win32-x64-msvc@0.0.1': + resolution: {integrity: sha512-sXTETqxi+6YRhaimZtPGvR3sDYLdAB9Mpj+SSzHrBPu8e15zExfWjZBHUAjzHr5j8YTLnw+UndgyP/fIB4Nclg==} + cpu: [x64] + os: [win32] + '@rolldown/binding-android-arm64@1.2.4': resolution: {integrity: sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -898,6 +948,9 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -1009,6 +1062,13 @@ packages: '@astrojs/markdown-remark': optional: true + autoprefixer@10.5.4: + resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -1020,6 +1080,15 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + baseline-browser-mapping@2.11.15: + resolution: {integrity: sha512-FwMjJJ7HnyZpWe+oWxegG0fezZyBZUagI5LZEoO3GCbtbKNwRfMH9Ue5d5v01PNePBy1QSfPSDTTeVL0Hb9EzA==} + engines: {node: '>=6.0.0'} + hasBin: true + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -1027,6 +1096,18 @@ packages: resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + caniuse-lite@1.0.30001809: + resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1040,6 +1121,10 @@ packages: character-entities-legacy@3.0.0: resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + chokidar@5.0.0: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} @@ -1127,6 +1212,10 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + dependency-graph@1.0.0: + resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==} + engines: {node: '>=4'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1158,6 +1247,9 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + dompurify@3.4.8: + resolution: {integrity: sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -1165,6 +1257,9 @@ packages: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} + electron-to-chromium@1.5.411: + resolution: {integrity: sha512-gglkxzokjHfawpGxq75XdBV2/l3BAPzrsMs70qgaZdTW5rpV1tC4MdgJVP9fN126bODA4ZJQkn1wryEzJyQXIg==} + enhanced-resolve@5.24.5: resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} engines: {node: '>=10.13.0'} @@ -1185,6 +1280,10 @@ packages: engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1290,6 +1389,10 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + find-process@2.1.1: resolution: {integrity: sha512-SrQDx3QhlmHM90iqn9rdjCQcw/T+WlpOkHFsjoRgB+zTpDfltNA1VSNYeYELwhUTJy12UFxqjWhmhOrJc+o4sA==} hasBin: true @@ -1316,6 +1419,9 @@ packages: resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} engines: {node: '>=20'} + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1328,6 +1434,10 @@ packages: github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -1393,6 +1503,10 @@ packages: iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-docker@4.0.0: resolution: {integrity: sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==} engines: {node: '>=20'} @@ -1406,6 +1520,10 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -1606,6 +1724,11 @@ packages: magicast@0.5.4: resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==} + marked@14.0.0: + resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==} + engines: {node: '>= 18'} + hasBin: true + mdast-util-to-hast@13.2.1: resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} @@ -1634,6 +1757,9 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} + monaco-editor@0.56.0: + resolution: {integrity: sha512-sXboRm3BeBeLm938eaiyLMe0OxzfXIlZvbv4ir/jVgQy1zDhWjgmny0WoN45fuDKhCCQsYMbBJrv/A6jd8aCUg==} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -1662,6 +1788,10 @@ packages: node-mock-http@1.0.5: resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==} + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -1737,10 +1867,19 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + postcss-nested@8.0.1: + resolution: {integrity: sha512-PvSIwDVh1NTw939iu4FBK+oeZfSm0cbErFAjXCTPiStammzobBNE2SiQNezy8Xp+Oud2uLXaLpTauv3UdxWjbQ==} + engines: {node: ^22.0.0 || ^24.0.0 || >= 26.0.0} + peerDependencies: + postcss: ^8.2.14 + postcss-selector-parser@7.1.5: resolution: {integrity: sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==} engines: {node: '>=4'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.26: resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} engines: {node: ^10 || ^12 || >=14} @@ -1758,6 +1897,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-hrtime@1.0.3: + resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} + engines: {node: '>= 0.8'} + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -1776,6 +1919,13 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + read-cache@1.0.2: + resolution: {integrity: sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + readdirp@5.1.1: resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} engines: {node: '>= 20.19.0'} @@ -1842,6 +1992,10 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + smol-toml@1.8.0: resolution: {integrity: sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==} engines: {node: '>= 18'} @@ -1894,6 +2048,10 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -2021,6 +2179,12 @@ packages: uploadthing: optional: true + update-browserslist-db@1.3.1: + resolution: {integrity: sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -2613,6 +2777,42 @@ snapshots: '@oxc-project/types@0.144.0': {} + '@postcss-go/core@0.0.1': + dependencies: + chokidar: 3.6.0 + dependency-graph: 1.0.0 + picocolors: 1.1.1 + pretty-hrtime: 1.0.3 + read-cache: 1.0.2 + slash: 5.1.0 + source-map-js: 1.2.1 + tinyglobby: 0.2.17 + optionalDependencies: + '@postcss-go/native-darwin-arm64': 0.0.1 + '@postcss-go/native-darwin-x64': 0.0.1 + '@postcss-go/native-linux-arm64-gnu': 0.0.1 + '@postcss-go/native-linux-x64-gnu': 0.0.1 + '@postcss-go/native-win32-arm64-msvc': 0.0.1 + '@postcss-go/native-win32-x64-msvc': 0.0.1 + + '@postcss-go/native-darwin-arm64@0.0.1': + optional: true + + '@postcss-go/native-darwin-x64@0.0.1': + optional: true + + '@postcss-go/native-linux-arm64-gnu@0.0.1': + optional: true + + '@postcss-go/native-linux-x64-gnu@0.0.1': + optional: true + + '@postcss-go/native-win32-arm64-msvc@0.0.1': + optional: true + + '@postcss-go/native-win32-x64-msvc@0.0.1': + optional: true + '@rolldown/binding-android-arm64@1.2.4': optional: true @@ -2792,6 +2992,9 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/trusted-types@2.0.7': + optional: true + '@types/unist@3.0.3': {} '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3)': @@ -3024,18 +3227,45 @@ snapshots: - uploadthing - yaml + autoprefixer@10.5.4(postcss@8.5.26): + dependencies: + browserslist: 4.28.8 + caniuse-lite: 1.0.30001809 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + axobject-query@4.1.0: {} bail@2.0.2: {} balanced-match@4.0.4: {} + baseline-browser-mapping@2.11.15: {} + + binary-extensions@2.3.0: {} + boolbase@1.0.0: {} brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.8: + dependencies: + baseline-browser-mapping: 2.11.15 + caniuse-lite: 1.0.30001809 + electron-to-chromium: 1.5.411 + node-releases: 2.0.53 + update-browserslist-db: 1.3.1(browserslist@4.28.8) + + caniuse-lite@1.0.30001809: {} + ccount@2.0.1: {} chalk@4.1.2: @@ -3047,6 +3277,18 @@ snapshots: character-entities-legacy@3.0.0: {} + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + chokidar@5.0.0: dependencies: readdirp: 5.1.1 @@ -3117,6 +3359,8 @@ snapshots: defu@6.1.7: {} + dependency-graph@1.0.0: {} + dequal@2.0.3: {} destr@2.0.5: {} @@ -3143,6 +3387,10 @@ snapshots: dependencies: domelementtype: 2.3.0 + dompurify@3.4.8: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -3151,6 +3399,8 @@ snapshots: dset@3.1.4: {} + electron-to-chromium@1.5.411: {} + enhanced-resolve@5.24.5: dependencies: graceful-fs: 4.2.11 @@ -3191,6 +3441,8 @@ snapshots: '@esbuild/win32-ia32': 0.28.2 '@esbuild/win32-x64': 0.28.2 + escalade@3.2.0: {} + escape-string-regexp@4.0.0: {} eslint-config-prettier@10.1.8(eslint@10.8.1(jiti@2.7.0)): @@ -3310,6 +3562,10 @@ snapshots: dependencies: flat-cache: 4.0.1 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + find-process@2.1.1: dependencies: chalk: 4.1.2 @@ -3338,6 +3594,8 @@ snapshots: dependencies: tiny-inflate: 1.0.3 + fraction.js@5.3.4: {} + fsevents@2.3.3: optional: true @@ -3347,6 +3605,10 @@ snapshots: github-slugger@2.0.0: {} + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -3435,6 +3697,10 @@ snapshots: iron-webcrypto@1.2.1: {} + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + is-docker@4.0.0: {} is-extglob@2.1.1: {} @@ -3443,6 +3709,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-number@7.0.0: {} + is-plain-obj@4.1.0: {} isexe@2.0.0: {} @@ -3598,6 +3866,8 @@ snapshots: '@babel/types': 7.29.8 source-map-js: 1.2.1 + marked@14.0.0: {} + mdast-util-to-hast@13.2.1: dependencies: '@types/hast': 3.0.5 @@ -3635,6 +3905,11 @@ snapshots: dependencies: brace-expansion: 5.0.9 + monaco-editor@0.56.0: + dependencies: + dompurify: 3.4.8 + marked: 14.0.0 + mrmime@2.0.1: {} ms@2.1.3: {} @@ -3653,6 +3928,8 @@ snapshots: node-mock-http@1.0.5: {} + node-releases@2.0.53: {} + normalize-path@3.0.0: {} nth-check@2.1.1: @@ -3723,11 +4000,18 @@ snapshots: picomatch@4.0.5: {} + postcss-nested@8.0.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 + postcss-selector-parser@7.1.5: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-value-parser@4.2.0: {} + postcss@8.5.26: dependencies: nanoid: 3.3.18 @@ -3744,6 +4028,8 @@ snapshots: prettier@3.9.6: {} + pretty-hrtime@1.0.3: {} + prismjs@1.30.0: {} process-ancestry@0.1.0: {} @@ -3754,6 +4040,12 @@ snapshots: radix3@1.1.2: {} + read-cache@1.0.2: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + readdirp@5.1.1: {} regex-recursion@6.0.2: @@ -3873,6 +4165,8 @@ snapshots: sisteransi@1.0.5: {} + slash@5.1.0: {} + smol-toml@1.8.0: {} source-map-js@1.2.1: {} @@ -3919,6 +4213,10 @@ snapshots: fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -4005,6 +4303,12 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.4 + update-browserslist-db@1.3.1(browserslist@4.28.8): + dependencies: + browserslist: 4.28.8 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 diff --git a/src/components/CodeSample.astro b/src/components/CodeSample.astro new file mode 100644 index 0000000..641bf09 --- /dev/null +++ b/src/components/CodeSample.astro @@ -0,0 +1,26 @@ +--- +interface Props { + filename: string; + code: string; +} + +const { filename, code } = Astro.props; +--- + +
+
+ {filename} + +
+
{code}
+
diff --git a/src/components/CssPlayground.astro b/src/components/CssPlayground.astro new file mode 100644 index 0000000..c6ff51a --- /dev/null +++ b/src/components/CssPlayground.astro @@ -0,0 +1,62 @@ +--- +interface Props { + id: string; + nested?: boolean; + autoprefixer?: boolean; +} + +const { id, nested = true, autoprefixer = true } = Astro.props; +--- + +
+
+
+ + +
+
+ + + +
+
+
+
+
Input
+
+
+
+
+
+ + +
+
+
+ +
+
+ +
+ + diff --git a/src/components/PlaygroundCta.astro b/src/components/PlaygroundCta.astro new file mode 100644 index 0000000..b57a15f --- /dev/null +++ b/src/components/PlaygroundCta.astro @@ -0,0 +1,12 @@ +--- +interface Props { + id: string; +} + +const { id } = Astro.props; +const href = `${import.meta.env.BASE_URL}playground/${id}/`; +--- + +

+ Open live playground → +

diff --git a/src/components/SiteNav.astro b/src/components/SiteNav.astro index ab4361e..aee4978 100644 --- a/src/components/SiteNav.astro +++ b/src/components/SiteNav.astro @@ -3,9 +3,11 @@ const base = import.meta.env.BASE_URL; const pathname = Astro.url.pathname; const homeHref = base; const architectureHref = `${base}architecture/`; -const progressHref = `${base}progress/`; +const examplesHref = `${base}examples/default/`; +const playgroundHref = `${base}playground/default/`; const guideHref = `${base}guide/get-started/`; -const isProgress = pathname === `${base}progress/` || pathname === `${base}progress`; +const isExamples = pathname === `${base}examples/` || pathname.startsWith(`${base}examples`); +const isPlayground = pathname === `${base}playground/` || pathname.startsWith(`${base}playground`); const isGuide = pathname.startsWith(`${base}guide`); const isArchitecture = pathname === `${base}architecture/` || pathname === `${base}architecture`; const navLink = 'rounded-full border border-transparent px-3 py-2 transition hover:text-acid'; @@ -68,9 +70,14 @@ const githubStars = await (async () => { aria-current={isGuide ? 'page' : undefined}>Guide ProgressExamples + Playground + +declare module '*?url' { + const value: string; + export default value; +} + +declare module '*?worker' { + const WorkerFactory: { + new (): Worker; + }; + export default WorkerFactory; +} diff --git a/src/layouts/ExamplesLayout.astro b/src/layouts/ExamplesLayout.astro new file mode 100644 index 0000000..8bb1e0e --- /dev/null +++ b/src/layouts/ExamplesLayout.astro @@ -0,0 +1,139 @@ +--- +import '../styles/global.css'; +import PageHead from '../components/PageHead.astro'; +import SiteNav from '../components/SiteNav.astro'; +import { exampleGroups } from '../lib/examples'; + +interface Props { + title?: string; + section?: string; + frontmatter?: { + title?: string; + section?: string; + }; +} + +const { frontmatter, title: propTitle, section: propSection } = Astro.props; +const title = frontmatter?.title ?? propTitle ?? ''; +const section = frontmatter?.section ?? propSection ?? ''; +const base = import.meta.env.BASE_URL; +const groups = exampleGroups(base); +--- + + + + + + +
+
+ +
+ +
+ +
+
+ + + diff --git a/src/layouts/PlaygroundLayout.astro b/src/layouts/PlaygroundLayout.astro new file mode 100644 index 0000000..25850af --- /dev/null +++ b/src/layouts/PlaygroundLayout.astro @@ -0,0 +1,64 @@ +--- +import '../styles/global.css'; +import PageHead from '../components/PageHead.astro'; +import SiteNav from '../components/SiteNav.astro'; +import { playgroundGroups } from '../lib/playgrounds'; + +interface Props { + title?: string; + description?: string; + section?: string; +} + +const { title = '', description = title, section = '' } = Astro.props; +const base = import.meta.env.BASE_URL; +const groups = playgroundGroups(base); +--- + + + + + + +
+
+ +
+ +
+ +
+
+ + diff --git a/src/lib/examples.ts b/src/lib/examples.ts new file mode 100644 index 0000000..71fc25c --- /dev/null +++ b/src/lib/examples.ts @@ -0,0 +1,41 @@ +export interface ExampleLink { + label: string; + href: string; + key: string; +} + +export interface ExampleGroup { + label: string; + items: ExampleLink[]; +} + +export function exampleGroups(base: string): ExampleGroup[] { + return [ + { + label: 'Usage', + items: [{ label: 'Default', href: `${base}examples/default/`, key: 'default' }], + }, + { + label: 'Frameworks', + items: [ + { label: 'Tailwind CSS', href: `${base}examples/tailwindcss/`, key: 'tailwindcss' }, + { label: 'Vite', href: `${base}examples/vite/`, key: 'vite' }, + { label: 'Next.js', href: `${base}examples/nextjs/`, key: 'nextjs' }, + { label: 'Nuxt', href: `${base}examples/nuxt/`, key: 'nuxt' }, + { label: 'Vue', href: `${base}examples/vue/`, key: 'vue' }, + { label: 'Astro', href: `${base}examples/astro/`, key: 'astro' }, + { label: 'SvelteKit', href: `${base}examples/sveltekit/`, key: 'sveltekit' }, + { label: 'webpack', href: `${base}examples/webpack/`, key: 'webpack' }, + ], + }, + { + label: 'Plugins', + items: [ + { label: 'Autoprefixer', href: `${base}examples/autoprefixer/`, key: 'autoprefixer' }, + { label: 'postcss-import', href: `${base}examples/import/`, key: 'import' }, + { label: 'postcss-nested', href: `${base}examples/nested/`, key: 'nested' }, + { label: 'cssnano', href: `${base}examples/cssnano/`, key: 'cssnano' }, + ], + }, + ]; +} diff --git a/src/lib/monaco-env.ts b/src/lib/monaco-env.ts new file mode 100644 index 0000000..bd154b5 --- /dev/null +++ b/src/lib/monaco-env.ts @@ -0,0 +1,17 @@ +import EditorWorker from 'monaco-editor/editor/editor.worker?worker'; +import CssWorker from 'monaco-editor/language/css/css.worker?worker'; + +const global = globalThis as typeof globalThis & { + MonacoEnvironment?: { + getWorker: (id: string, label: string) => Worker; + }; +}; + +global.MonacoEnvironment = { + getWorker(_id, label) { + if (label === 'css' || label === 'scss' || label === 'less') { + return new CssWorker(); + } + return new EditorWorker(); + }, +}; diff --git a/src/lib/playground-app.ts b/src/lib/playground-app.ts new file mode 100644 index 0000000..683159c --- /dev/null +++ b/src/lib/playground-app.ts @@ -0,0 +1,226 @@ +import './monaco-env'; +import * as monaco from 'monaco-editor/editor/editor.api'; +import 'monaco-editor/language/css/monaco.contribution'; + +import { processCss } from './playground-process'; +import { playgroundPreset, type PluginFlags } from './playgrounds'; + +const DARK: monaco.editor.IStandaloneThemeData = { + base: 'vs-dark', + inherit: true, + rules: [], + colors: { + 'editor.background': '#0a0b0d', + 'editor.foreground': '#f5f5f0', + 'editorLineNumber.foreground': '#56606b', + 'editorCursor.foreground': '#c8ff3d', + focusBorder: '#c8ff3d', + }, +}; + +const LIGHT: monaco.editor.IStandaloneThemeData = { + base: 'vs', + inherit: true, + rules: [], + colors: { + 'editor.background': '#f5f5f0', + 'editor.foreground': '#0a0b0d', + 'editorCursor.foreground': '#527800', + }, +}; + +function currentTheme() { + return document.documentElement.dataset.theme === 'light' + ? 'postcss-go-light' + : 'postcss-go-dark'; +} + +function decodeHash() { + const raw = location.hash.replace(/^#/, ''); + if (!raw) return ''; + const css = new URLSearchParams(raw).get('css'); + if (!css) return ''; + try { + return new TextDecoder().decode( + Uint8Array.from(atob(css.replace(/-/g, '+').replace(/_/g, '/')), (c) => c.charCodeAt(0)), + ); + } catch { + return ''; + } +} + +function encodeCss(css: string) { + const bytes = new TextEncoder().encode(css); + let binary = ''; + bytes.forEach((byte) => { + binary += String.fromCharCode(byte); + }); + return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); +} + +function pluginFlags(root: HTMLElement): PluginFlags { + return { + nested: Boolean(root.querySelector('[data-plugin="nested"]')?.checked), + autoprefixer: Boolean( + root.querySelector('[data-plugin="autoprefixer"]')?.checked, + ), + }; +} + +function previewDocument(css: string, body: string) { + const light = document.documentElement.dataset.theme === 'light'; + const background = light ? '#f5f5f0' : '#0a0b0d'; + const color = light ? '#0a0b0d' : '#f5f5f0'; + return `${body}`; +} + +export function bootPlayground(root: HTMLElement) { + const id = root.dataset.presetId ?? 'default'; + const preset = playgroundPreset(id); + const inputHost = root.querySelector('[data-editor="input"]'); + const outputHost = root.querySelector('[data-editor="output"]'); + const preview = root.querySelector('[data-preview]'); + const status = root.querySelector('[data-status]'); + const errorBox = root.querySelector('[data-error]'); + const share = root.querySelector('[data-share]'); + const reset = root.querySelector('[data-reset]'); + const copy = root.querySelector('[data-copy]'); + if (!inputHost || !outputHost) return; + + monaco.editor.defineTheme('postcss-go-dark', DARK); + monaco.editor.defineTheme('postcss-go-light', LIGHT); + + const options: monaco.editor.IStandaloneEditorConstructionOptions = { + language: 'css', + theme: currentTheme(), + fontSize: 13, + fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, monospace', + minimap: { enabled: false }, + scrollBeyondLastLine: false, + automaticLayout: true, + padding: { top: 12, bottom: 12 }, + wordWrap: 'on', + tabSize: 2, + renderLineHighlight: 'line', + }; + + const input = monaco.editor.create(inputHost, { + ...options, + value: decodeHash() || preset.input, + }); + const output = monaco.editor.create(outputHost, { + ...options, + value: '', + readOnly: true, + domReadOnly: true, + }); + + let timer = 0; + let latestCss = ''; + + const observer = new MutationObserver(() => { + const theme = currentTheme(); + monaco.editor.setTheme(theme); + input.updateOptions({ theme }); + output.updateOptions({ theme }); + if (latestCss && preview) { + preview.srcdoc = previewDocument(latestCss, preset.previewHtml); + } + }); + observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); + + async function run() { + const css = input.getValue(); + status && (status.textContent = 'Running…'); + errorBox && (errorBox.hidden = true); + monaco.editor.setModelMarkers(input.getModel()!, 'postcss-go', []); + try { + const result = await processCss(css, pluginFlags(root)); + latestCss = result.css; + output.setValue(result.css); + if (preview) { + preview.srcdoc = previewDocument(result.css, preset.previewHtml); + } + if (status) { + status.textContent = `${result.backend} · ${result.ms.toFixed(1)}ms`; + } + } catch (error) { + const err = error as { message?: string; reason?: string; line?: number; column?: number }; + const message = err.reason || err.message || String(error); + latestCss = ''; + output.setValue(''); + if (errorBox) { + errorBox.hidden = false; + errorBox.textContent = message; + } + if (status) status.textContent = 'error'; + if (err.line && input.getModel()) { + monaco.editor.setModelMarkers(input.getModel()!, 'postcss-go', [ + { + startLineNumber: err.line, + startColumn: err.column || 1, + endLineNumber: err.line, + endColumn: (err.column || 1) + 1, + message, + severity: monaco.MarkerSeverity.Error, + }, + ]); + } + } + } + + function schedule() { + window.clearTimeout(timer); + timer = window.setTimeout(() => { + void run(); + }, 200); + } + + input.onDidChangeModelContent(schedule); + root.querySelectorAll('[data-plugin]').forEach((box) => { + box.addEventListener('change', () => { + void run(); + }); + }); + + root.querySelectorAll('[data-tab]').forEach((button) => { + button.addEventListener('click', () => { + const tab = button.dataset.tab; + root.querySelectorAll('[data-tab]').forEach((item) => { + item.setAttribute('aria-selected', String(item === button)); + }); + root.querySelectorAll('[data-panel]').forEach((panel) => { + panel.hidden = panel.dataset.panel !== tab; + }); + }); + }); + + share?.addEventListener('click', async () => { + const url = new URL(location.href); + url.hash = new URLSearchParams({ css: encodeCss(input.getValue()) }).toString(); + await navigator.clipboard.writeText(url.toString()); + share.textContent = 'Copied link'; + window.setTimeout(() => { + share.textContent = 'Share'; + }, 1400); + }); + + reset?.addEventListener('click', () => { + history.replaceState(null, '', location.pathname + location.search); + input.setValue(preset.input); + void run(); + }); + + copy?.addEventListener('click', async () => { + await navigator.clipboard.writeText(latestCss); + copy.textContent = 'Copied'; + window.setTimeout(() => { + copy.textContent = 'Copy output'; + }, 1400); + }); + + void run(); +} diff --git a/src/lib/playground-process.ts b/src/lib/playground-process.ts new file mode 100644 index 0000000..db2aff5 --- /dev/null +++ b/src/lib/playground-process.ts @@ -0,0 +1,41 @@ +import { createBrowserProcessor, type BrowserProcessor } from '@postcss-go/core/wasm'; +import workerUrl from '@postcss-go/core/wasm/worker?url'; +import wasmUrl from '@postcss-go/core/wasm/postcss-go.wasm?url'; +import wasmExecUrl from '@postcss-go/core/wasm/wasm_exec.js?url'; +import autoprefixer from 'autoprefixer'; +import nested from 'postcss-nested'; + +import type { PluginFlags } from './playgrounds'; + +type BrowserPlugins = NonNullable[0]>; + +let processor: BrowserProcessor | undefined; +let processorKey = ''; + +function pluginList(flags: PluginFlags): BrowserPlugins { + const plugins: BrowserPlugins = []; + if (flags.nested) plugins.push(nested()); + if (flags.autoprefixer) plugins.push(autoprefixer()); + return plugins; +} + +export async function processCss(css: string, flags: PluginFlags) { + const key = `${flags.nested}:${flags.autoprefixer}`; + if (!processor || processorKey !== key) { + await processor?.close(); + processor = createBrowserProcessor(pluginList(flags), { + workerUrl, + wasmUrl, + wasmExecUrl, + }); + processorKey = key; + } + + const started = performance.now(); + const result = await processor.process(css, { from: 'input.css', map: false }); + return { + css: result.css, + backend: result.backend ?? 'wasm-worker', + ms: performance.now() - started, + }; +} diff --git a/src/lib/playgrounds.ts b/src/lib/playgrounds.ts new file mode 100644 index 0000000..1885210 --- /dev/null +++ b/src/lib/playgrounds.ts @@ -0,0 +1,276 @@ +export interface PlaygroundLink { + id: string; + label: string; +} + +export interface PluginFlags { + nested: boolean; + autoprefixer: boolean; +} + +export interface PlaygroundPreset { + id: string; + title: string; + description: string; + exampleId: string; + input: string; + previewHtml: string; + plugins: PluginFlags; +} + +export const PLAYGROUND_LINKS: PlaygroundLink[] = [ + { id: 'default', label: 'Default' }, + { id: 'tailwindcss', label: 'Tailwind CSS' }, + { id: 'vite', label: 'Vite' }, + { id: 'nextjs', label: 'Next.js' }, + { id: 'nuxt', label: 'Nuxt' }, + { id: 'vue', label: 'Vue' }, + { id: 'astro', label: 'Astro' }, + { id: 'sveltekit', label: 'SvelteKit' }, + { id: 'webpack', label: 'webpack' }, +]; + +export function playgroundGroups(base: string) { + return [ + { + label: 'Frameworks', + items: PLAYGROUND_LINKS.map((item) => ({ + label: item.label, + href: `${base}playground/${item.id}/`, + key: item.id, + })), + }, + ]; +} + +export function playgroundPreset(id: string): PlaygroundPreset { + const preset = PLAYGROUND_PRESETS[id] ?? PLAYGROUND_PRESETS.default; + if (!preset) { + throw new Error(`Unknown playground: ${id}`); + } + return preset; +} + +const demoInput = `.hero { + display: flex; + gap: 1rem; + user-select: none; + backdrop-filter: blur(12px); + + & h1 { + margin: 0; + letter-spacing: -0.06em; + } + + & .cta { + appearance: none; + border-radius: 999px; + } +} +`; + +export const PLAYGROUND_PRESETS: Record = { + default: { + id: 'default', + title: 'Default', + description: 'Parse, run plugins, stringify — the same pipeline as the CLI.', + exampleId: 'default', + plugins: { nested: true, autoprefixer: true }, + input: demoInput, + previewHtml: `

PostCSS, rebuilt.

`, + }, + tailwindcss: { + id: 'tailwindcss', + title: 'Tailwind CSS', + description: + 'Sample CSS through nested + Autoprefixer. Tailwind itself is not compiled here — it needs a project file tree.', + exampleId: 'tailwindcss', + plugins: { nested: true, autoprefixer: true }, + input: `.page { + display: grid; + min-height: 100vh; + place-items: center; + background: #0a0b0d; + color: #f5f5f0; + + & .mark { + color: #c8ff3d; + } + + & .btn { + appearance: none; + user-select: none; + border-radius: 999px; + background: #c8ff3d; + color: #0a0b0d; + } +} +`, + previewHtml: `

Tailwind-shaped CSS

Utility first.

`, + }, + vite: { + id: 'vite', + title: 'Vite', + description: 'Imported CSS as Vite would send it through PostCSS.', + exampleId: 'vite', + plugins: { nested: true, autoprefixer: true }, + input: `#app { + min-height: 100vh; + display: grid; + place-items: center; + + & .card { + display: flex; + gap: 0.75rem; + backdrop-filter: blur(8px); + user-select: none; + } +} +`, + previewHtml: `
src/style.css
`, + }, + nextjs: { + id: 'nextjs', + title: 'Next.js', + description: 'Global stylesheet shape used from app/layout.', + exampleId: 'nextjs', + plugins: { nested: true, autoprefixer: true }, + input: `:root { + --fg: #f5f5f0; + --bg: #0a0b0d; +} + +body { + margin: 0; + background: var(--bg); + color: var(--fg); +} + +.hero { + display: grid; + min-height: 100vh; + place-items: center; + + & p { + user-select: none; + } +} +`, + previewHtml: `

Next.js

app/globals.css

`, + }, + nuxt: { + id: 'nuxt', + title: 'Nuxt', + description: 'Asset CSS with nested rules, as Nuxt PostCSS would see it.', + exampleId: 'nuxt', + plugins: { nested: true, autoprefixer: true }, + input: `body { + margin: 0; + font-family: system-ui, sans-serif; +} + +.hero { + display: flex; + min-height: 100vh; + align-items: center; + justify-content: center; + + & h1 { + letter-spacing: -0.05em; + } +} +`, + previewHtml: `

Nuxt + PostCSS

`, + }, + vue: { + id: 'vue', + title: 'Vue', + description: 'Component-style nested CSS, including scoped-looking selectors.', + exampleId: 'vue', + plugins: { nested: true, autoprefixer: true }, + input: `.hero { + display: grid; + min-height: 100vh; + place-items: center; + + & .title { + user-select: none; + letter-spacing: 0.02em; + } +} +`, + previewHtml: `

Vue scoped styles

`, + }, + astro: { + id: 'astro', + title: 'Astro', + description: 'Global CSS plus nested blocks, matching Astro style pipelines.', + exampleId: 'astro', + plugins: { nested: true, autoprefixer: true }, + input: `:root { + --bg: #0a0b0d; +} + +html { + background: var(--bg); +} + +.hero { + display: grid; + min-height: 100vh; + place-items: center; + + & .card { + appearance: none; + user-select: none; + } +} +`, + previewHtml: `
src/styles/global.css
`, + }, + sveltekit: { + id: 'sveltekit', + title: 'SvelteKit', + description: 'Imported app.css after vitePreprocess would run PostCSS.', + exampleId: 'sveltekit', + plugins: { nested: true, autoprefixer: true }, + input: `:root { + font-family: system-ui, sans-serif; +} + +.hero { + display: flex; + min-height: 100vh; + align-items: center; + justify-content: center; + gap: 1rem; + + & h1 { + letter-spacing: -0.06em; + } +} +`, + previewHtml: `

SvelteKit

`, + }, + webpack: { + id: 'webpack', + title: 'webpack', + description: 'CSS as css-loader + postcss-loader would hand to the engine.', + exampleId: 'webpack', + plugins: { nested: true, autoprefixer: true }, + input: `.hero { + display: flex; + gap: 0.5rem; + min-height: 100vh; + align-items: center; + justify-content: center; + user-select: none; + + & h1 span { + color: #c8ff3d; + } +} +`, + previewHtml: `

webpack + PostCSS

`, + }, +}; diff --git a/src/pages/examples/astro.astro b/src/pages/examples/astro.astro new file mode 100644 index 0000000..a8d9a76 --- /dev/null +++ b/src/pages/examples/astro.astro @@ -0,0 +1,64 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const scaffold = 'pnpm create astro@latest my-app'; +const install = 'pnpm add -D @postcss-go/core autoprefixer'; +const config = `export default { + plugins: { + autoprefixer: {}, + }, +};`; +const css = `@import "./tokens.css"; + +:root { + --bg: #0a0b0d; +} + +html { + background: var(--bg); +}`; +const astroFile = `--- +import '../styles/global.css'; +--- + + + + + + + +`; +const scripts = `{ + "scripts": { + "dev": "astro dev", + "build": "pnpm build:css && astro build", + "build:css": "postcss-go src/styles/**/*.css --base src/styles --dir dist/css" + } +}`; +--- + + +

Astro

+

+ Astro's Vite layer loads postcss.config.mjs or .js from the project root. + Component <style> blocks and imported global CSS both go through that config. +

+ + + + + + + +

+ Tailwind v4 in Astro is often @tailwindcss/vite instead of PostCSS. For the PostCSS plugin + path, use the Tailwind CSS{' '} + example. +

+
diff --git a/src/pages/examples/autoprefixer.astro b/src/pages/examples/autoprefixer.astro new file mode 100644 index 0000000..412606b --- /dev/null +++ b/src/pages/examples/autoprefixer.astro @@ -0,0 +1,21 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; + +const install = 'pnpm add -D @postcss-go/core autoprefixer'; +const config = `export default { + plugins: { + autoprefixer: {}, + }, +};`; +const cli = 'pnpm postcss-go src/index.css -u autoprefixer -o dist/index.css'; +--- + + +

Autoprefixer

+

The smallest useful plugin chain: parse with Go, run Autoprefixer, stringify with Go.

+ + +

Or skip the config file and load the plugin from the CLI:

+ +
diff --git a/src/pages/examples/cssnano.astro b/src/pages/examples/cssnano.astro new file mode 100644 index 0000000..df20eeb --- /dev/null +++ b/src/pages/examples/cssnano.astro @@ -0,0 +1,31 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; + +const install = 'pnpm add -D @postcss-go/core postcss-import autoprefixer cssnano'; +const config = `export default (ctx) => ({ + map: ctx.env === 'production' ? { inline: false } : true, + plugins: { + 'postcss-import': {}, + autoprefixer: {}, + cssnano: ctx.env === 'production' ? {} : false, + }, +});`; +const scripts = `{ + "scripts": { + "build:css": "postcss-go src/index.css -o dist/index.css --env production --no-map", + "watch:css": "postcss-go src/index.css -o dist/index.css -w" + } +}`; +--- + + +

cssnano

+

+ Disable cssnano in development with false. Function configs receive + ctx.env (NODE_ENV, then development). +

+ + + +
diff --git a/src/pages/examples/default.astro b/src/pages/examples/default.astro new file mode 100644 index 0000000..0e85d3b --- /dev/null +++ b/src/pages/examples/default.astro @@ -0,0 +1,110 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const install = 'pnpm add -D @postcss-go/core autoprefixer'; +const config = `export default { + plugins: { + autoprefixer: {}, + }, +};`; +const functionConfig = `export default (ctx) => ({ + map: ctx.env === 'production' ? { inline: false } : true, + plugins: { + autoprefixer: {}, + }, +});`; +const css = `:root { + color-scheme: dark; +} + +.button { + display: flex; + gap: 0.5rem; +}`; +const scripts = `{ + "scripts": { + "build:css": "postcss-go src/index.css -o dist/index.css", + "watch:css": "postcss-go src/index.css -o dist/index.css -w" + } +}`; +const api = `import { readFile, writeFile } from 'node:fs/promises'; +import postcss from '@postcss-go/core'; +import autoprefixer from 'autoprefixer'; + +const css = await readFile('src/index.css', 'utf8'); +const result = await postcss([autoprefixer()]).process(css, { + from: 'src/index.css', + to: 'dist/index.css', +}); + +await writeFile('dist/index.css', result.css);`; +--- + + +

Default

+

+ The default setup is a postcss.config.js plus the postcss-go CLI. No bundler + is required. The same config file is what Vite, Next.js, and other tools already look for. +

+ + +

Install

+ + +

Configuration

+

+ Object form is enough for a fixed plugin list. false disables an entry; + true calls the plugin with no options. +

+ +

+ Use a function when maps or plugins depend on NODE_ENV or the current file. + ctx.env is NODE_ENV, then development. +

+ + +

Input and scripts

+ + + +

JavaScript API

+

+ Skip the CLI when you already own the file I/O. process() always returns a Promise — + there is no implicit LazyResult. +

+ + +

CLI

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TaskCommand
One filepostcss-go src/index.css -o dist/index.css
Directory / globpostcss-go src/**/*.css --base src --dir dist
Watchpostcss-go src/index.css -o dist/index.css -w
Plugin without a configpostcss-go src/index.css -u autoprefixer -o dist/index.css
Disable mapspostcss-go src/index.css -o dist/index.css --no-map
+
diff --git a/src/pages/examples/import.astro b/src/pages/examples/import.astro new file mode 100644 index 0000000..f691e10 --- /dev/null +++ b/src/pages/examples/import.astro @@ -0,0 +1,30 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; + +const install = 'pnpm add -D @postcss-go/core postcss-import'; +const config = `export default { + plugins: { + 'postcss-import': {}, + }, +};`; +const css = `@import "./tokens.css"; +@import "./components/card.css"; + +.page { + color: var(--fg); +}`; +const build = 'pnpm postcss-go src/index.css -o dist/index.css'; +--- + + +

postcss-import

+

+ Inlines @import files and records dependency messages so watch mode can rebuild when + a partial changes. Put postcss-import first so later plugins see the concatenated CSS. +

+ + + + +
diff --git a/src/pages/examples/index.astro b/src/pages/examples/index.astro new file mode 100644 index 0000000..9c690c4 --- /dev/null +++ b/src/pages/examples/index.astro @@ -0,0 +1,3 @@ +--- +return Astro.redirect(`${import.meta.env.BASE_URL}examples/default/`); +--- diff --git a/src/pages/examples/nested.astro b/src/pages/examples/nested.astro new file mode 100644 index 0000000..c42dc21 --- /dev/null +++ b/src/pages/examples/nested.astro @@ -0,0 +1,36 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; + +const install = 'pnpm add -D @postcss-go/core postcss-nested'; +const config = `export default { + plugins: { + 'postcss-nested': {}, + }, +};`; +const css = `.card { + padding: 1rem; + + & .title { + font-weight: 700; + } + + &:hover { + background: #111; + } +}`; +const build = 'pnpm postcss-go src/index.css -o dist/index.css'; +--- + + +

postcss-nested

+

+ Unwraps nested rules into standard CSS. Combine with + postcss-import when nested files are split across partials + — import first, then nest. +

+ + + + +
diff --git a/src/pages/examples/nextjs.astro b/src/pages/examples/nextjs.astro new file mode 100644 index 0000000..893e87e --- /dev/null +++ b/src/pages/examples/nextjs.astro @@ -0,0 +1,56 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const scaffold = 'pnpm create next-app@latest my-app'; +const install = 'pnpm add -D @postcss-go/core autoprefixer'; +const config = `const config = { + plugins: { + autoprefixer: {}, + }, +}; + +export default config;`; +const css = `@import "./tokens.css"; + +:root { + --fg: #111; +} + +body { + color: var(--fg); +}`; +const scripts = `{ + "scripts": { + "dev": "next dev", + "build": "pnpm build:css && next build", + "build:css": "postcss-go src/**/*.css --base src --dir public/css --no-map" + } +}`; +--- + + +

Next.js

+

+ Next.js picks up postcss.config.mjs, .js, or .cjs at the project + root. The default plugin list is Autoprefixer; import global CSS from + app/layout.tsx or pages/_app.tsx. +

+ + + + + +

+ next dev / next build still run Next's CSS pipeline. Use the CLI when you + want postcss-go to emit CSS (for example into public/) with the same config. +

+ +

+ The create-next-app Tailwind option writes a Tailwind PostCSS config. Match that stack with the Tailwind CSS example. +

+
diff --git a/src/pages/examples/nuxt.astro b/src/pages/examples/nuxt.astro new file mode 100644 index 0000000..73a84c9 --- /dev/null +++ b/src/pages/examples/nuxt.astro @@ -0,0 +1,56 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const scaffold = 'pnpm dlx nuxi@latest init my-app'; +const install = 'pnpm add -D @postcss-go/core postcss-import autoprefixer'; +const nuxtConfig = `export default defineNuxtConfig({ + css: ['~/assets/css/main.css'], + postcss: { + plugins: { + 'postcss-import': {}, + autoprefixer: {}, + }, + }, +});`; +const fileConfig = `export default { + plugins: { + 'postcss-import': {}, + autoprefixer: {}, + }, +};`; +const css = `@import "./tokens.css"; + +body { + margin: 0; + font-family: system-ui, sans-serif; +}`; +const scripts = `{ + "scripts": { + "dev": "nuxt dev", + "build": "pnpm build:css && nuxt build", + "build:css": "postcss-go assets/css/main.css -o public/css/main.css --no-map" + } +}`; +--- + + +

Nuxt

+

+ Nuxt 3/4 reads PostCSS plugins from nuxt.config. A root + postcss.config.js works too — use one place, not both, so the plugin list stays in sync. +

+ + + + +

Equivalent file config, which postcss-go loads directly:

+ + + +

+ For Tailwind, replace the plugin list with the one from the + Tailwind CSS example. +

+
diff --git a/src/pages/examples/sveltekit.astro b/src/pages/examples/sveltekit.astro new file mode 100644 index 0000000..d57a0fb --- /dev/null +++ b/src/pages/examples/sveltekit.astro @@ -0,0 +1,66 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const scaffold = 'pnpm create svelte@latest my-app'; +const install = 'pnpm add -D @postcss-go/core autoprefixer'; +const svelteConfig = `import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +export default { + preprocess: vitePreprocess(), + kit: { + adapter: adapter(), + }, +};`; +const config = `export default { + plugins: { + autoprefixer: {}, + }, +};`; +const css = `@import "./tokens.css"; + +:root { + font-family: system-ui, sans-serif; +}`; +const svelteFile = ` + +

Hello

+ +`; +const scripts = `{ + "scripts": { + "dev": "vite dev", + "build": "pnpm build:css && vite build", + "build:css": "postcss-go src/**/*.css --base src --dir static/css" + } +}`; +--- + + +

SvelteKit

+

+ Enable vitePreprocess() so <style> blocks run through PostCSS, then + add the default config at the repo root. SvelteKit uses Vite, so the same + postcss.config.js applies to imported CSS files. +

+ + + + + + + + +

+ For Tailwind, use the plugin list from the + Tailwind CSS example. +

+
diff --git a/src/pages/examples/tailwindcss.astro b/src/pages/examples/tailwindcss.astro new file mode 100644 index 0000000..be7848a --- /dev/null +++ b/src/pages/examples/tailwindcss.astro @@ -0,0 +1,89 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const installV4 = 'pnpm add -D @postcss-go/core tailwindcss @tailwindcss/postcss'; +const configV4 = `export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +};`; +const cssV4 = `@import "tailwindcss"; +@source "./**/*.{html,js,ts,jsx,tsx}";`; +const build = 'pnpm postcss-go src/index.css -o dist/index.css'; + +const installV3 = 'pnpm add -D @postcss-go/core tailwindcss@3 autoprefixer'; +const configV3 = `export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +};`; +const tailwindConfigV3 = `/** @type {import('tailwindcss').Config} */ +export default { + content: ['./src/**/*.{html,js,ts,jsx,tsx}'], + theme: { + extend: {}, + }, + plugins: [], +};`; +const cssV3 = `@tailwind base; +@tailwind components; +@tailwind utilities;`; +--- + + +

Tailwind CSS

+

+ Run Tailwind as a PostCSS plugin through the Go engine. Pick v4 or v3, then process CSS with + postcss-go. +

+ + +
+
+ + +
+ +
+

+ Tailwind v4's PostCSS plugin lives in @tailwindcss/postcss. Autoprefixer is not + required; Tailwind's optimizer handles vendor prefixes. When you run Tailwind through the + CLI instead of a bundler, tell it which files to scan with @source. +

+ + + + +
+ + +
+
diff --git a/src/pages/examples/vite.astro b/src/pages/examples/vite.astro new file mode 100644 index 0000000..4dbf850 --- /dev/null +++ b/src/pages/examples/vite.astro @@ -0,0 +1,49 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const scaffold = 'pnpm create vite@latest my-app'; +const install = 'pnpm add -D @postcss-go/core autoprefixer'; +const config = `export default { + plugins: { + autoprefixer: {}, + }, +};`; +const css = `@import "./tokens.css"; + +#app { + min-height: 100vh; +}`; +const scripts = `{ + "scripts": { + "dev": "vite", + "build": "pnpm build:css && vite build", + "build:css": "postcss-go src/**/*.css --base src --dir dist/css", + "watch:css": "postcss-go src/**/*.css --base src --dir dist/css -w" + } +}`; +--- + + +

Vite

+

+ Vite loads postcss.config.js from the project root automatically. Use that file for the + plugin list, then run the same config through postcss-go when you want the Go engine. +

+ + + + +

+ No Vite config is required for PostCSS. Import CSS from JS as usual; add a CLI script when CSS + should be compiled by postcss-go instead of (or before) vite build. +

+ + +

+ For Tailwind, put the plugin list from the + Tailwind CSS example into this config. Vue, React, + and Svelte Vite apps use the same file. +

+
diff --git a/src/pages/examples/vue.astro b/src/pages/examples/vue.astro new file mode 100644 index 0000000..90c881b --- /dev/null +++ b/src/pages/examples/vue.astro @@ -0,0 +1,60 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const scaffold = 'pnpm create vue@latest my-app'; +const install = 'pnpm add -D @postcss-go/core autoprefixer'; +const config = `export default { + plugins: { + autoprefixer: {}, + }, +};`; +const css = `@import "./tokens.css"; + +#app { + max-width: 72rem; + margin: 0 auto; +}`; +const main = `import './assets/main.css'; +import { createApp } from 'vue'; +import App from './App.vue'; + +createApp(App).mount('#app');`; +const vueFile = ` + +`; +const scripts = `{ + "scripts": { + "dev": "vite", + "build": "pnpm build:css && vite build", + "build:css": "postcss-go src/assets/**/*.css --base src/assets --dir dist/css" + } +}`; +--- + + +

Vue

+

+ Vue 3 apps from create-vue are Vite projects. Put the default PostCSS config at the repo + root; Vite applies it to imported CSS and <style> blocks. +

+ + + + + + + + +

+ Vue CLI (webpack) uses the same postcss.config.js. For Tailwind, see + Tailwind CSS. +

+
diff --git a/src/pages/examples/webpack.astro b/src/pages/examples/webpack.astro new file mode 100644 index 0000000..3ff2d12 --- /dev/null +++ b/src/pages/examples/webpack.astro @@ -0,0 +1,63 @@ +--- +import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; +import CodeSample from '../../components/CodeSample.astro'; +import PlaygroundCta from '../../components/PlaygroundCta.astro'; + +const install = + 'pnpm add -D @postcss-go/core autoprefixer webpack webpack-cli css-loader style-loader postcss-loader'; +const config = `export default { + plugins: { + autoprefixer: {}, + }, +};`; +const webpackConfig = `import postcssGo from '@postcss-go/core'; + +export default { + module: { + rules: [ + { + test: /\\.css$/i, + use: [ + 'style-loader', + 'css-loader', + { + loader: 'postcss-loader', + options: { + implementation: postcssGo, + }, + }, + ], + }, + ], + }, +};`; +const css = `.button { + display: flex; + gap: 0.5rem; +}`; +const scripts = `{ + "scripts": { + "build": "webpack --mode production", + "build:css": "postcss-go src/**/*.css --base src --dir dist/css --no-map" + } +}`; +--- + + +

webpack

+

+ Point postcss-loader at @postcss-go/core with + implementation so the Go engine runs inside the webpack CSS rule. Keep a root + postcss.config.js for the plugin list — the loader reads it automatically. +

+ + + + + +

+ process() on postcss-go is always a Promise. Use postcss-loader v7 or newer, which awaits + the result. You can still run the CLI for a bundler-free CSS pass: +

+ +
diff --git a/src/pages/guide/get-started.md b/src/pages/guide/get-started.md index 426297b..fbd38fb 100644 --- a/src/pages/guide/get-started.md +++ b/src/pages/guide/get-started.md @@ -50,6 +50,8 @@ file or environment:
terminal
pnpm postcss-go src/index.css -o dist/index.css
+For the default config, Tailwind CSS, Vite, Next.js, and other stacks, see +[Examples](../../examples/default/). For a gradual migration, see the [migration and compatibility guide](../migration/). For browsers, import `@postcss-go/core/wasm` and follow the [Browser and WASM](../browser-wasm/) guide. diff --git a/src/pages/index.astro b/src/pages/index.astro index b060655..c538d0a 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -67,7 +67,12 @@ import BenchmarkChart from '../components/BenchmarkChart.astro'; Get started - Progress + Examples + Playground MIT licensed diff --git a/src/pages/playground/[id].astro b/src/pages/playground/[id].astro new file mode 100644 index 0000000..3e15f39 --- /dev/null +++ b/src/pages/playground/[id].astro @@ -0,0 +1,24 @@ +--- +import PlaygroundLayout from '../../layouts/PlaygroundLayout.astro'; +import CssPlayground from '../../components/CssPlayground.astro'; +import { PLAYGROUND_LINKS, playgroundPreset } from '../../lib/playgrounds'; + +export function getStaticPaths() { + return PLAYGROUND_LINKS.map((item) => ({ params: { id: item.id } })); +} + +const id = Astro.params.id ?? 'default'; +const spec = playgroundPreset(id); +const exampleHref = `${import.meta.env.BASE_URL}examples/${spec.exampleId}/`; +--- + + +
+

{spec.title}

+

+ {spec.description} Runs the @postcss-go/core WASM worker in this tab — the same engine + as the {spec.title} example. +

+
+ +
diff --git a/src/pages/playground/index.astro b/src/pages/playground/index.astro new file mode 100644 index 0000000..52653ab --- /dev/null +++ b/src/pages/playground/index.astro @@ -0,0 +1,3 @@ +--- +return Astro.redirect(`${import.meta.env.BASE_URL}playground/default/`); +--- diff --git a/src/styles/global.css b/src/styles/global.css index 805159d..3969df2 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -335,3 +335,230 @@ html[data-theme='light'] .site-nav__github:hover { html[data-theme='light'] .site-nav__github { border-color: rgba(10, 11, 13, 0.2) !important; } + +.playground-page { + display: flex; + min-height: 100dvh; + flex-direction: column; +} + +@media (min-width: 761px) { + .playground-page { + height: 100dvh; + overflow: hidden; + } +} + +.playground-shell { + display: grid; + width: 100%; + max-width: 100rem; + min-height: 0; + flex: 1; + grid-template-columns: 1fr; + gap: clamp(1.5rem, 4vw, 2.5rem); + padding: 0 1.5rem 1.5rem; +} + +@media (min-width: 761px) { + .playground-shell { + grid-template-columns: 13rem minmax(0, 1fr); + padding: 0 2.5rem 1.25rem; + } +} + +.playground-aside { + min-width: 0; +} + +@media (min-width: 761px) { + .playground-aside { + overflow: auto; + padding-bottom: 0.5rem; + } +} + +.playground-main { + display: flex; + min-width: 0; + min-height: 0; + flex-direction: column; +} + +.playground-intro h1 { + margin: 0; + font-size: clamp(1.35rem, 2.4vw, 1.85rem); + letter-spacing: -0.04em; + line-height: 1.15; +} + +.playground-intro p { + margin: 0.45rem 0 0.9rem; + max-width: 46rem; + color: var(--copy-muted); + font-size: 0.9rem; + line-height: 1.55; +} + +.playground-intro code { + border: 1px solid var(--line); + border-radius: 0.3rem; + background: var(--surface); + padding: 0.08rem 0.32rem; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; + font-size: 0.86em; + color: var(--accent-readable); +} + +.playground { + display: flex; + min-height: 28rem; + flex: 1; + flex-direction: column; + overflow: hidden; + border: 1px solid var(--line); + border-radius: 0.85rem; + background: var(--surface-soft); +} + +@media (min-width: 761px) { + .playground { + min-height: 0; + } +} + +.playground__toolbar, +.playground__footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.75rem 1rem; + padding: 0.65rem 0.9rem; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; + font-size: 0.68rem; +} + +.playground__toolbar { + border-bottom: 1px solid var(--line-soft); +} + +.playground__footer { + border-top: 1px solid var(--line-soft); + color: var(--copy-faint); +} + +.playground__plugins, +.playground__actions, +.playground__tabs { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem 0.85rem; +} + +.playground__check { + display: inline-flex; + align-items: center; + gap: 0.4rem; + color: var(--copy-muted); + cursor: pointer; + letter-spacing: 0.04em; + text-transform: lowercase; +} + +.playground__check input { + accent-color: var(--accent-readable); +} + +.playground__actions button, +.playground__tabs button { + border: 1px solid var(--line); + border-radius: 999px; + background: transparent; + padding: 0.28rem 0.75rem; + color: var(--copy-muted); + cursor: pointer; + font: inherit; + letter-spacing: 0.06em; + text-transform: uppercase; + transition: + border-color 150ms, + color 150ms; +} + +.playground__actions button:hover, +.playground__tabs button:hover, +.playground__tabs button[aria-selected='true'] { + border-color: var(--accent-readable); + color: var(--accent-readable); +} + +.playground__panes { + display: grid; + min-height: 0; + flex: 1; + grid-template-columns: 1fr; +} + +@media (min-width: 900px) { + .playground__panes { + grid-template-columns: 1fr 1fr; + } +} + +.playground__pane { + display: flex; + min-height: 16rem; + min-width: 0; + flex-direction: column; +} + +@media (min-width: 900px) { + .playground__pane { + min-height: 0; + } + + .playground__pane + .playground__pane { + border-left: 1px solid var(--line-soft); + } +} + +.playground__label { + padding: 0.5rem 0.9rem 0.35rem; + color: var(--copy-faint); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; + font-size: 0.65rem; + letter-spacing: 0.14em; + text-transform: uppercase; +} + +.playground__label--tabs { + padding-bottom: 0.25rem; +} + +.playground__editor, +.playground__preview { + min-height: 0; + flex: 1; +} + +.playground__preview { + width: 100%; + border: 0; + background: var(--canvas); +} + +.playground__error { + color: #ff6b6b; +} + +.playground .monaco-editor, +.playground .monaco-editor-background, +.playground .margin { + background: transparent !important; +} + +html[data-theme='light'] .playground__error { + color: #b42318; +} From 43c12cc1bdac73194ff5c1bce144ed16f6008eb7 Mon Sep 17 00:00:00 2001 From: eryue0220 Date: Thu, 27 Aug 2026 00:36:19 +0800 Subject: [PATCH 2/2] add webpack example --- src/lib/examples.ts | 20 +----- src/lib/playgrounds.ts | 2 +- src/pages/examples/astro.astro | 64 ------------------- src/pages/examples/autoprefixer.astro | 21 ------- src/pages/examples/cssnano.astro | 31 ---------- src/pages/examples/import.astro | 30 --------- src/pages/examples/nested.astro | 36 ----------- src/pages/examples/nextjs.astro | 56 ----------------- src/pages/examples/nuxt.astro | 56 ----------------- src/pages/examples/sveltekit.astro | 66 -------------------- src/pages/examples/tailwindcss.astro | 89 --------------------------- src/pages/examples/vite.astro | 49 --------------- src/pages/examples/vue.astro | 60 ------------------ src/pages/examples/webpack.astro | 39 +++++++++--- src/pages/guide/get-started.md | 4 +- 15 files changed, 33 insertions(+), 590 deletions(-) delete mode 100644 src/pages/examples/astro.astro delete mode 100644 src/pages/examples/autoprefixer.astro delete mode 100644 src/pages/examples/cssnano.astro delete mode 100644 src/pages/examples/import.astro delete mode 100644 src/pages/examples/nested.astro delete mode 100644 src/pages/examples/nextjs.astro delete mode 100644 src/pages/examples/nuxt.astro delete mode 100644 src/pages/examples/sveltekit.astro delete mode 100644 src/pages/examples/tailwindcss.astro delete mode 100644 src/pages/examples/vite.astro delete mode 100644 src/pages/examples/vue.astro diff --git a/src/lib/examples.ts b/src/lib/examples.ts index 71fc25c..7db1043 100644 --- a/src/lib/examples.ts +++ b/src/lib/examples.ts @@ -17,25 +17,7 @@ export function exampleGroups(base: string): ExampleGroup[] { }, { label: 'Frameworks', - items: [ - { label: 'Tailwind CSS', href: `${base}examples/tailwindcss/`, key: 'tailwindcss' }, - { label: 'Vite', href: `${base}examples/vite/`, key: 'vite' }, - { label: 'Next.js', href: `${base}examples/nextjs/`, key: 'nextjs' }, - { label: 'Nuxt', href: `${base}examples/nuxt/`, key: 'nuxt' }, - { label: 'Vue', href: `${base}examples/vue/`, key: 'vue' }, - { label: 'Astro', href: `${base}examples/astro/`, key: 'astro' }, - { label: 'SvelteKit', href: `${base}examples/sveltekit/`, key: 'sveltekit' }, - { label: 'webpack', href: `${base}examples/webpack/`, key: 'webpack' }, - ], - }, - { - label: 'Plugins', - items: [ - { label: 'Autoprefixer', href: `${base}examples/autoprefixer/`, key: 'autoprefixer' }, - { label: 'postcss-import', href: `${base}examples/import/`, key: 'import' }, - { label: 'postcss-nested', href: `${base}examples/nested/`, key: 'nested' }, - { label: 'cssnano', href: `${base}examples/cssnano/`, key: 'cssnano' }, - ], + items: [{ label: 'webpack', href: `${base}examples/webpack/`, key: 'webpack' }], }, ]; } diff --git a/src/lib/playgrounds.ts b/src/lib/playgrounds.ts index 1885210..58ed996 100644 --- a/src/lib/playgrounds.ts +++ b/src/lib/playgrounds.ts @@ -255,7 +255,7 @@ html { webpack: { id: 'webpack', title: 'webpack', - description: 'CSS as css-loader + postcss-loader would hand to the engine.', + description: 'CSS as @postcss-go/webpack-loader would hand to the engine.', exampleId: 'webpack', plugins: { nested: true, autoprefixer: true }, input: `.hero { diff --git a/src/pages/examples/astro.astro b/src/pages/examples/astro.astro deleted file mode 100644 index a8d9a76..0000000 --- a/src/pages/examples/astro.astro +++ /dev/null @@ -1,64 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; -import PlaygroundCta from '../../components/PlaygroundCta.astro'; - -const scaffold = 'pnpm create astro@latest my-app'; -const install = 'pnpm add -D @postcss-go/core autoprefixer'; -const config = `export default { - plugins: { - autoprefixer: {}, - }, -};`; -const css = `@import "./tokens.css"; - -:root { - --bg: #0a0b0d; -} - -html { - background: var(--bg); -}`; -const astroFile = `--- -import '../styles/global.css'; ---- - - - - - - - -`; -const scripts = `{ - "scripts": { - "dev": "astro dev", - "build": "pnpm build:css && astro build", - "build:css": "postcss-go src/styles/**/*.css --base src/styles --dir dist/css" - } -}`; ---- - - -

Astro

-

- Astro's Vite layer loads postcss.config.mjs or .js from the project root. - Component <style> blocks and imported global CSS both go through that config. -

- - - - - - - -

- Tailwind v4 in Astro is often @tailwindcss/vite instead of PostCSS. For the PostCSS plugin - path, use the Tailwind CSS{' '} - example. -

-
diff --git a/src/pages/examples/autoprefixer.astro b/src/pages/examples/autoprefixer.astro deleted file mode 100644 index 412606b..0000000 --- a/src/pages/examples/autoprefixer.astro +++ /dev/null @@ -1,21 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; - -const install = 'pnpm add -D @postcss-go/core autoprefixer'; -const config = `export default { - plugins: { - autoprefixer: {}, - }, -};`; -const cli = 'pnpm postcss-go src/index.css -u autoprefixer -o dist/index.css'; ---- - - -

Autoprefixer

-

The smallest useful plugin chain: parse with Go, run Autoprefixer, stringify with Go.

- - -

Or skip the config file and load the plugin from the CLI:

- -
diff --git a/src/pages/examples/cssnano.astro b/src/pages/examples/cssnano.astro deleted file mode 100644 index df20eeb..0000000 --- a/src/pages/examples/cssnano.astro +++ /dev/null @@ -1,31 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; - -const install = 'pnpm add -D @postcss-go/core postcss-import autoprefixer cssnano'; -const config = `export default (ctx) => ({ - map: ctx.env === 'production' ? { inline: false } : true, - plugins: { - 'postcss-import': {}, - autoprefixer: {}, - cssnano: ctx.env === 'production' ? {} : false, - }, -});`; -const scripts = `{ - "scripts": { - "build:css": "postcss-go src/index.css -o dist/index.css --env production --no-map", - "watch:css": "postcss-go src/index.css -o dist/index.css -w" - } -}`; ---- - - -

cssnano

-

- Disable cssnano in development with false. Function configs receive - ctx.env (NODE_ENV, then development). -

- - - -
diff --git a/src/pages/examples/import.astro b/src/pages/examples/import.astro deleted file mode 100644 index f691e10..0000000 --- a/src/pages/examples/import.astro +++ /dev/null @@ -1,30 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; - -const install = 'pnpm add -D @postcss-go/core postcss-import'; -const config = `export default { - plugins: { - 'postcss-import': {}, - }, -};`; -const css = `@import "./tokens.css"; -@import "./components/card.css"; - -.page { - color: var(--fg); -}`; -const build = 'pnpm postcss-go src/index.css -o dist/index.css'; ---- - - -

postcss-import

-

- Inlines @import files and records dependency messages so watch mode can rebuild when - a partial changes. Put postcss-import first so later plugins see the concatenated CSS. -

- - - - -
diff --git a/src/pages/examples/nested.astro b/src/pages/examples/nested.astro deleted file mode 100644 index c42dc21..0000000 --- a/src/pages/examples/nested.astro +++ /dev/null @@ -1,36 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; - -const install = 'pnpm add -D @postcss-go/core postcss-nested'; -const config = `export default { - plugins: { - 'postcss-nested': {}, - }, -};`; -const css = `.card { - padding: 1rem; - - & .title { - font-weight: 700; - } - - &:hover { - background: #111; - } -}`; -const build = 'pnpm postcss-go src/index.css -o dist/index.css'; ---- - - -

postcss-nested

-

- Unwraps nested rules into standard CSS. Combine with - postcss-import when nested files are split across partials - — import first, then nest. -

- - - - -
diff --git a/src/pages/examples/nextjs.astro b/src/pages/examples/nextjs.astro deleted file mode 100644 index 893e87e..0000000 --- a/src/pages/examples/nextjs.astro +++ /dev/null @@ -1,56 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; -import PlaygroundCta from '../../components/PlaygroundCta.astro'; - -const scaffold = 'pnpm create next-app@latest my-app'; -const install = 'pnpm add -D @postcss-go/core autoprefixer'; -const config = `const config = { - plugins: { - autoprefixer: {}, - }, -}; - -export default config;`; -const css = `@import "./tokens.css"; - -:root { - --fg: #111; -} - -body { - color: var(--fg); -}`; -const scripts = `{ - "scripts": { - "dev": "next dev", - "build": "pnpm build:css && next build", - "build:css": "postcss-go src/**/*.css --base src --dir public/css --no-map" - } -}`; ---- - - -

Next.js

-

- Next.js picks up postcss.config.mjs, .js, or .cjs at the project - root. The default plugin list is Autoprefixer; import global CSS from - app/layout.tsx or pages/_app.tsx. -

- - - - - -

- next dev / next build still run Next's CSS pipeline. Use the CLI when you - want postcss-go to emit CSS (for example into public/) with the same config. -

- -

- The create-next-app Tailwind option writes a Tailwind PostCSS config. Match that stack with the Tailwind CSS example. -

-
diff --git a/src/pages/examples/nuxt.astro b/src/pages/examples/nuxt.astro deleted file mode 100644 index 73a84c9..0000000 --- a/src/pages/examples/nuxt.astro +++ /dev/null @@ -1,56 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; -import PlaygroundCta from '../../components/PlaygroundCta.astro'; - -const scaffold = 'pnpm dlx nuxi@latest init my-app'; -const install = 'pnpm add -D @postcss-go/core postcss-import autoprefixer'; -const nuxtConfig = `export default defineNuxtConfig({ - css: ['~/assets/css/main.css'], - postcss: { - plugins: { - 'postcss-import': {}, - autoprefixer: {}, - }, - }, -});`; -const fileConfig = `export default { - plugins: { - 'postcss-import': {}, - autoprefixer: {}, - }, -};`; -const css = `@import "./tokens.css"; - -body { - margin: 0; - font-family: system-ui, sans-serif; -}`; -const scripts = `{ - "scripts": { - "dev": "nuxt dev", - "build": "pnpm build:css && nuxt build", - "build:css": "postcss-go assets/css/main.css -o public/css/main.css --no-map" - } -}`; ---- - - -

Nuxt

-

- Nuxt 3/4 reads PostCSS plugins from nuxt.config. A root - postcss.config.js works too — use one place, not both, so the plugin list stays in sync. -

- - - - -

Equivalent file config, which postcss-go loads directly:

- - - -

- For Tailwind, replace the plugin list with the one from the - Tailwind CSS example. -

-
diff --git a/src/pages/examples/sveltekit.astro b/src/pages/examples/sveltekit.astro deleted file mode 100644 index d57a0fb..0000000 --- a/src/pages/examples/sveltekit.astro +++ /dev/null @@ -1,66 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; -import PlaygroundCta from '../../components/PlaygroundCta.astro'; - -const scaffold = 'pnpm create svelte@latest my-app'; -const install = 'pnpm add -D @postcss-go/core autoprefixer'; -const svelteConfig = `import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - -export default { - preprocess: vitePreprocess(), - kit: { - adapter: adapter(), - }, -};`; -const config = `export default { - plugins: { - autoprefixer: {}, - }, -};`; -const css = `@import "./tokens.css"; - -:root { - font-family: system-ui, sans-serif; -}`; -const svelteFile = ` - -

Hello

- -`; -const scripts = `{ - "scripts": { - "dev": "vite dev", - "build": "pnpm build:css && vite build", - "build:css": "postcss-go src/**/*.css --base src --dir static/css" - } -}`; ---- - - -

SvelteKit

-

- Enable vitePreprocess() so <style> blocks run through PostCSS, then - add the default config at the repo root. SvelteKit uses Vite, so the same - postcss.config.js applies to imported CSS files. -

- - - - - - - - -

- For Tailwind, use the plugin list from the - Tailwind CSS example. -

-
diff --git a/src/pages/examples/tailwindcss.astro b/src/pages/examples/tailwindcss.astro deleted file mode 100644 index be7848a..0000000 --- a/src/pages/examples/tailwindcss.astro +++ /dev/null @@ -1,89 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; -import PlaygroundCta from '../../components/PlaygroundCta.astro'; - -const installV4 = 'pnpm add -D @postcss-go/core tailwindcss @tailwindcss/postcss'; -const configV4 = `export default { - plugins: { - '@tailwindcss/postcss': {}, - }, -};`; -const cssV4 = `@import "tailwindcss"; -@source "./**/*.{html,js,ts,jsx,tsx}";`; -const build = 'pnpm postcss-go src/index.css -o dist/index.css'; - -const installV3 = 'pnpm add -D @postcss-go/core tailwindcss@3 autoprefixer'; -const configV3 = `export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -};`; -const tailwindConfigV3 = `/** @type {import('tailwindcss').Config} */ -export default { - content: ['./src/**/*.{html,js,ts,jsx,tsx}'], - theme: { - extend: {}, - }, - plugins: [], -};`; -const cssV3 = `@tailwind base; -@tailwind components; -@tailwind utilities;`; ---- - - -

Tailwind CSS

-

- Run Tailwind as a PostCSS plugin through the Go engine. Pick v4 or v3, then process CSS with - postcss-go. -

- - -
-
- - -
- -
-

- Tailwind v4's PostCSS plugin lives in @tailwindcss/postcss. Autoprefixer is not - required; Tailwind's optimizer handles vendor prefixes. When you run Tailwind through the - CLI instead of a bundler, tell it which files to scan with @source. -

- - - - -
- - -
-
diff --git a/src/pages/examples/vite.astro b/src/pages/examples/vite.astro deleted file mode 100644 index 4dbf850..0000000 --- a/src/pages/examples/vite.astro +++ /dev/null @@ -1,49 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; -import PlaygroundCta from '../../components/PlaygroundCta.astro'; - -const scaffold = 'pnpm create vite@latest my-app'; -const install = 'pnpm add -D @postcss-go/core autoprefixer'; -const config = `export default { - plugins: { - autoprefixer: {}, - }, -};`; -const css = `@import "./tokens.css"; - -#app { - min-height: 100vh; -}`; -const scripts = `{ - "scripts": { - "dev": "vite", - "build": "pnpm build:css && vite build", - "build:css": "postcss-go src/**/*.css --base src --dir dist/css", - "watch:css": "postcss-go src/**/*.css --base src --dir dist/css -w" - } -}`; ---- - - -

Vite

-

- Vite loads postcss.config.js from the project root automatically. Use that file for the - plugin list, then run the same config through postcss-go when you want the Go engine. -

- - - - -

- No Vite config is required for PostCSS. Import CSS from JS as usual; add a CLI script when CSS - should be compiled by postcss-go instead of (or before) vite build. -

- - -

- For Tailwind, put the plugin list from the - Tailwind CSS example into this config. Vue, React, - and Svelte Vite apps use the same file. -

-
diff --git a/src/pages/examples/vue.astro b/src/pages/examples/vue.astro deleted file mode 100644 index 90c881b..0000000 --- a/src/pages/examples/vue.astro +++ /dev/null @@ -1,60 +0,0 @@ ---- -import ExamplesLayout from '../../layouts/ExamplesLayout.astro'; -import CodeSample from '../../components/CodeSample.astro'; -import PlaygroundCta from '../../components/PlaygroundCta.astro'; - -const scaffold = 'pnpm create vue@latest my-app'; -const install = 'pnpm add -D @postcss-go/core autoprefixer'; -const config = `export default { - plugins: { - autoprefixer: {}, - }, -};`; -const css = `@import "./tokens.css"; - -#app { - max-width: 72rem; - margin: 0 auto; -}`; -const main = `import './assets/main.css'; -import { createApp } from 'vue'; -import App from './App.vue'; - -createApp(App).mount('#app');`; -const vueFile = ` - -`; -const scripts = `{ - "scripts": { - "dev": "vite", - "build": "pnpm build:css && vite build", - "build:css": "postcss-go src/assets/**/*.css --base src/assets --dir dist/css" - } -}`; ---- - - -

Vue

-

- Vue 3 apps from create-vue are Vite projects. Put the default PostCSS config at the repo - root; Vite applies it to imported CSS and <style> blocks. -

- - - - - - - - -

- Vue CLI (webpack) uses the same postcss.config.js. For Tailwind, see - Tailwind CSS. -

-
diff --git a/src/pages/examples/webpack.astro b/src/pages/examples/webpack.astro index 3ff2d12..96541b3 100644 --- a/src/pages/examples/webpack.astro +++ b/src/pages/examples/webpack.astro @@ -4,13 +4,23 @@ import CodeSample from '../../components/CodeSample.astro'; import PlaygroundCta from '../../components/PlaygroundCta.astro'; const install = - 'pnpm add -D @postcss-go/core autoprefixer webpack webpack-cli css-loader style-loader postcss-loader'; + 'pnpm add -D @postcss-go/core @postcss-go/webpack-loader autoprefixer webpack webpack-cli css-loader style-loader'; const config = `export default { plugins: { autoprefixer: {}, }, };`; -const webpackConfig = `import postcssGo from '@postcss-go/core'; +const webpackConfig = `export default { + module: { + rules: [ + { + test: /\\.css$/i, + use: ['style-loader', 'css-loader', '@postcss-go/webpack-loader'], + }, + ], + }, +};`; +const webpackConfigInline = `import autoprefixer from 'autoprefixer'; export default { module: { @@ -21,9 +31,13 @@ export default { 'style-loader', 'css-loader', { - loader: 'postcss-loader', + loader: '@postcss-go/webpack-loader', options: { - implementation: postcssGo, + sourceMap: true, + postcssOptions: { + config: false, + plugins: [autoprefixer()], + }, }, }, ], @@ -46,18 +60,23 @@ const scripts = `{

webpack

- Point postcss-loader at @postcss-go/core with - implementation so the Go engine runs inside the webpack CSS rule. Keep a root - postcss.config.js for the plugin list — the loader reads it automatically. + Add @postcss-go/webpack-loader to your CSS rule. It calls the Go engine directly and + does not depend on postcss or the official postcss-loader. By default + it discovers postcss.config.js from the input file, same as the CLI.

-

- process() on postcss-go is always a Promise. Use postcss-loader v7 or newer, which awaits - the result. You can still run the CLI for a bundler-free CSS pass: + Pass postcssOptions inline when you want to disable config lookup or set plugins in the + webpack config. postcssOptions can also be a function that receives mode, + env, file, and the loader context.

+ + +

You can still run the CLI for a bundler-free CSS pass:

diff --git a/src/pages/guide/get-started.md b/src/pages/guide/get-started.md index fbd38fb..f16b00d 100644 --- a/src/pages/guide/get-started.md +++ b/src/pages/guide/get-started.md @@ -50,8 +50,8 @@ file or environment:
terminal
pnpm postcss-go src/index.css -o dist/index.css
-For the default config, Tailwind CSS, Vite, Next.js, and other stacks, see -[Examples](../../examples/default/). +For the default config, see [Examples](../../examples/default/). For webpack, +see the [webpack example](../../examples/webpack/). For a gradual migration, see the [migration and compatibility guide](../migration/). For browsers, import `@postcss-go/core/wasm` and follow the [Browser and WASM](../browser-wasm/) guide.