Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
});
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
304 changes: 304 additions & 0 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions src/components/CodeSample.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
interface Props {
filename: string;
code: string;
}

const { filename, code } = Astro.props;
---

<div
class="mb-12 mt-7 overflow-hidden rounded-[.85rem] border border-white/10 bg-transparent"
data-code-sample
>
<div
class="flex items-center justify-between gap-4 border-b border-white/[.08] px-[1.1rem] py-[.7rem]"
>
<span class="font-mono text-[.68rem] tracking-[.08em] text-white/50">{filename}</span>
<button
class="shrink-0 cursor-pointer rounded-full border border-white/10 bg-transparent px-[.7rem] py-[.35rem] font-mono text-[.68rem] text-white/70 transition-colors duration-150 hover:border-acid hover:text-acid focus-visible:border-acid focus-visible:text-acid focus-visible:outline-none"
type="button"
data-copy-code>Copy</button
>
</div>
<pre
class="m-0 rounded-none border-0 px-[1.1rem] py-5"><code class="select-text whitespace-pre rounded-none border-0 bg-transparent p-0 text-[.9rem] leading-[inherit] text-inherit">{code}</code></pre>
</div>
62 changes: 62 additions & 0 deletions src/components/CssPlayground.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
interface Props {
id: string;
nested?: boolean;
autoprefixer?: boolean;
}

const { id, nested = true, autoprefixer = true } = Astro.props;
---

<div class="playground" data-playground data-preset-id={id}>
<div class="playground__toolbar">
<div class="playground__plugins" role="group" aria-label="Plugins">
<label class="playground__check">
<input type="checkbox" data-plugin="nested" checked={nested} />
nested
</label>
<label class="playground__check">
<input type="checkbox" data-plugin="autoprefixer" checked={autoprefixer} />
autoprefixer
</label>
</div>
<div class="playground__actions">
<button type="button" data-reset>Reset</button>
<button type="button" data-share>Share</button>
<button type="button" data-copy>Copy output</button>
</div>
</div>
<div class="playground__panes">
<section class="playground__pane">
<div class="playground__label">Input</div>
<div class="playground__editor" data-editor="input"></div>
</section>
<section class="playground__pane">
<div class="playground__label playground__label--tabs">
<div class="playground__tabs" role="tablist" aria-label="Output">
<button type="button" role="tab" data-tab="output" aria-selected="true">Output</button>
<button type="button" role="tab" data-tab="preview" aria-selected="false">Preview</button>
</div>
</div>
<div class="playground__editor" data-editor="output" data-panel="output"></div>
<iframe
class="playground__preview"
data-preview
data-panel="preview"
title="CSS preview"
sandbox
hidden></iframe>
</section>
</div>
<div class="playground__footer">
<span data-status>Loading WASM…</span>
<span class="playground__error" data-error hidden></span>
</div>
</div>

<script>
import { bootPlayground } from '../lib/playground-app';

const root = document.querySelector<HTMLElement>('[data-playground]');
if (root) bootPlayground(root);
</script>
12 changes: 12 additions & 0 deletions src/components/PlaygroundCta.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
interface Props {
id: string;
}

const { id } = Astro.props;
const href = `${import.meta.env.BASE_URL}playground/${id}/`;
---

<p>
<a class="text-acid" href={href}>Open live playground →</a>
</p>
17 changes: 12 additions & 5 deletions src/components/SiteNav.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -68,9 +70,14 @@ const githubStars = await (async () => {
aria-current={isGuide ? 'page' : undefined}>Guide</a
>
<a
class:list={[isProgress ? activeNavLink : navLink, 'site-nav__link']}
href={progressHref}
aria-current={isProgress ? 'page' : undefined}>Progress</a
class:list={[isExamples ? activeNavLink : navLink, 'site-nav__link']}
href={examplesHref}
aria-current={isExamples ? 'page' : undefined}>Examples</a
>
<a
class:list={[isPlayground ? activeNavLink : navLink, 'site-nav__link']}
href={playgroundHref}
aria-current={isPlayground ? 'page' : undefined}>Playground</a
>
<a
class:list={[isArchitecture ? activeNavLink : navLink, 'site-nav__link']}
Expand Down
13 changes: 13 additions & 0 deletions src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference types="astro/client" />

declare module '*?url' {
const value: string;
export default value;
}

declare module '*?worker' {
const WorkerFactory: {
new (): Worker;
};
export default WorkerFactory;
}
139 changes: 139 additions & 0 deletions src/layouts/ExamplesLayout.astro
Original file line number Diff line number Diff line change
@@ -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);
---

<html lang="en">
<head>
<PageHead title={`${title} · Examples · postcss-go`} description={title} />
</head>
<body>
<div class="fixed inset-0 -z-10 grid-bg"></div>
<div class="fixed inset-0 -z-10 noise"></div>
<SiteNav />
<main
class="mx-auto grid max-w-7xl grid-cols-1 gap-[clamp(3.5rem,9vw,8rem)] px-6 pb-24 pt-8 min-[761px]:grid-cols-[15rem_minmax(0,1fr)] lg:px-10 lg:pt-16"
>
<aside
class="mb-10 self-start min-[761px]:sticky min-[761px]:top-8 min-[761px]:mb-0"
aria-label="Example frameworks"
>
<div class="mb-4 font-mono text-[.68rem] uppercase tracking-[.14em] text-acid">
Examples
</div>
<nav
class="grid gap-8 border-t border-white/10 pt-[.95rem] min-[761px]:border-l min-[761px]:border-t-0 min-[761px]:pl-4 min-[761px]:pt-0"
>
{
groups.map((group) => (
<div>
<div class="mb-3 font-mono text-[.65rem] uppercase tracking-[.12em] text-white/35">
{group.label}
</div>
<div class="flex gap-5 overflow-x-auto whitespace-nowrap min-[761px]:grid min-[761px]:gap-[.45rem] min-[761px]:overflow-visible min-[761px]:whitespace-normal">
{group.items.map((link) => (
<a
class:list={[
'py-[.35rem] text-[.9rem] leading-normal text-white/50 transition-colors duration-150 hover:text-acid!',
section === link.key && 'text-acid!',
]}
href={link.href}
aria-current={section === link.key ? 'page' : undefined}
>
{link.label}
</a>
))}
</div>
</div>
))
}
</nav>
</aside>
<article class="progress-content guide-content min-w-0 max-w-none min-[761px]:max-w-[52rem]">
<slot />
</article>
</main>
<script>
async function copyText(value: string, button: HTMLButtonElement) {
try {
await navigator.clipboard.writeText(value);
} catch {
const selection = window.getSelection();
const range = document.createRange();
const target =
button.closest('[data-code-sample], [data-install-panel]')?.querySelector('code') ||
button.parentElement?.querySelector('code');
if (target) {
range.selectNodeContents(target);
selection?.removeAllRanges();
selection?.addRange(range);
document.execCommand('copy');
selection?.removeAllRanges();
}
}
button.textContent = 'Copied';
window.setTimeout(() => {
button.textContent = 'Copy';
}, 1400);
}

document.querySelectorAll<HTMLElement>('[data-code-sample]').forEach((sample) => {
const button = sample.querySelector<HTMLButtonElement>('[data-copy-code]');
const code = sample.querySelector('code');
if (!button || !code) return;

button.addEventListener('click', async () => {
await copyText(code.textContent || '', button);
});
});

document.querySelectorAll<HTMLElement>('[data-install-tabs]').forEach((tabs) => {
const buttons = Array.from(tabs.querySelectorAll<HTMLButtonElement>('[data-install-tab]'));
const panels = Array.from(tabs.querySelectorAll<HTMLElement>('[data-install-panel]'));
buttons.forEach((button, index) => {
button.addEventListener('click', () => {
const selected = button.dataset.installTab;
if (!selected) return;
buttons.forEach((item) => {
const active = item === button;
item.setAttribute('aria-selected', String(active));
item.tabIndex = active ? 0 : -1;
});
panels.forEach((panel) => {
panel.hidden = panel.dataset.installPanel !== selected;
});
});
button.addEventListener('keydown', (event) => {
if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft') return;
event.preventDefault();
const direction = event.key === 'ArrowRight' ? 1 : -1;
buttons[(index + direction + buttons.length) % buttons.length]?.focus();
});
});
tabs.querySelectorAll<HTMLButtonElement>('[data-install-copy]').forEach((button) => {
button.addEventListener('click', async () => {
const code = button.parentElement?.querySelector('code');
await copyText(code?.textContent || '', button);
});
});
});
</script>
</body>
</html>
64 changes: 64 additions & 0 deletions src/layouts/PlaygroundLayout.astro
Original file line number Diff line number Diff line change
@@ -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);
---

<html lang="en" class="h-full">
<head>
<PageHead title={`${title} · Playground · postcss-go`} description={description} />
</head>
<body class="playground-page">
<div class="fixed inset-0 -z-10 grid-bg"></div>
<div class="fixed inset-0 -z-10 noise"></div>
<SiteNav />
<main class="playground-shell">
<aside class="playground-aside" aria-label="Playground frameworks">
<div class="mb-4 font-mono text-[.68rem] uppercase tracking-[.14em] text-acid">
Playground
</div>
<nav
class="grid gap-8 border-t border-white/10 pt-[.95rem] min-[761px]:border-l min-[761px]:border-t-0 min-[761px]:pl-4 min-[761px]:pt-0"
>
{
groups.map((group) => (
<div>
<div class="mb-3 font-mono text-[.65rem] uppercase tracking-[.12em] text-white/35">
{group.label}
</div>
<div class="flex gap-5 overflow-x-auto whitespace-nowrap min-[761px]:grid min-[761px]:gap-[.45rem] min-[761px]:overflow-visible min-[761px]:whitespace-normal">
{group.items.map((link) => (
<a
class:list={[
'py-[.35rem] text-[.9rem] leading-normal text-white/50 transition-colors duration-150 hover:text-acid!',
section === link.key && 'text-acid!',
]}
href={link.href}
aria-current={section === link.key ? 'page' : undefined}
>
{link.label}
</a>
))}
</div>
</div>
))
}
</nav>
</aside>
<div class="playground-main">
<slot />
</div>
</main>
</body>
</html>
Loading
Loading