diff --git a/.gitignore b/.gitignore index ed7273e..aeb16ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -.scratch-build-cache +# scratch build cache and config +.scratch/cache/ # dependencies (bun install) node_modules diff --git a/.scratch/project.toml b/.scratch/project.toml new file mode 100644 index 0000000..40a34eb --- /dev/null +++ b/.scratch/project.toml @@ -0,0 +1,13 @@ +# Scratch Cloud Project Configuration +# +# This file configures how your project deploys to Scratch Cloud. +# Run `scratch cloud config` to update these settings interactively. + +# Project name +name = "scratch-dev" + +# Server URL (overrides global default) +server_url = "https://app.scratch.dev" + +# Visibility +visibility = "public" diff --git a/pages/components/BouncingDvdLogo.tsx b/pages/components/BouncingDvdLogo.tsx index ec3cf37..6dcc5b7 100644 --- a/pages/components/BouncingDvdLogo.tsx +++ b/pages/components/BouncingDvdLogo.tsx @@ -99,7 +99,7 @@ export default function BouncingDvdLogo() { }, [velocity]); return ( -
+
{/* TV outer frame */}
, og:title, twitter:title) +description: "Complete documentation for the Scratch CLI" # SEO description +keywords: ["MDX", "static site", "React", "Bun", "markdown"] # SEO keywords +author: "Pete Koomen" # Page author +robots: "index, follow" # Search engine directives +lang: "en" # HTML lang attribute +canonical: "https://scratch.dev/docs/" # Canonical URL () + +# Open Graph (social sharing) +image: "https://scratch.dev/scratch-social.png" # Social sharing image (og:image, twitter:image) +url: "https://scratch.dev/docs/" # Open Graph URL (og:url) +type: "website" # Content type (og:type) - "website" or "article" +siteName: "Scratch" # Site name (og:site_name) +locale: "en_US" # Locale (og:locale) +siteUrl: "https://scratch.dev" # Base URL for resolving relative image paths --- # Documentation @@ -38,9 +47,9 @@ A simple Scratch project (created with `scratch create my-scratch-project`) look ``` my-scratch-project/ - pages/ # put markdown and components here + pages/ # put markdown pages and components here posts/ (collapsed) - post1.md # accessible at /posts/post1 + post1.md # accessible at /posts/post1 post2.md # /posts/post2 blog.png # static assets in pages/ or public/ index.mdx # accessible at root path (/) @@ -53,7 +62,12 @@ my-scratch-project/ CodeBlock.tsx Heading.tsx Link.tsx - PageWrapper.jsx # wraps every page + template/ (collapsed) # page template components + Copyright.jsx + Footer.jsx + Header.jsx + PageWrapper.jsx # wraps every page + ScratchBadge.jsx tailwind.css # global styles AGENTS.md # agent context package.json # dependencies @@ -87,9 +101,10 @@ Add static content like a favicon or `_redirects` file to the `public/` director `src/` is for CSS files, components and JS/TS libraries. New Scratch projects will contain the following: -- `src/PageWrapper.jsx` - your Markdown contents will be "wrapped" with this component. Modify it to change page headers, footers, nav bars and other global components. +- `src/template/PageWrapper.jsx` - your Markdown contents will be "wrapped" with this component. Modify it to change page headers, footers, nav bars and other global components. +- `src/template/` - contains page template components like `Header.jsx`, `Footer.jsx`, `Copyright.jsx`, and `ScratchBadge.jsx`. - `src/tailwind.css` - global styles. Edit this to change the look and feel of your compiled Markdown. -- `src/markdown` - a directory containing default Markdown components. For example, edit `src/markdown/CodeBlock.tsx` to change how Markdown code blocks are rendered. +- `src/markdown` - a directory containing default Markdown components. For example, edit `src/markdown/CodeBlock.tsx` to change how Markdown code blocks are rendered. ### package.json @@ -217,19 +232,70 @@ These options work with all commands: ### Frontmatter -Add YAML frontmatter to set page metadata: +Add YAML frontmatter at the top of your MDX files to set page metadata. These properties are automatically converted to HTML meta tags for SEO and social sharing. -```mdx +#### Basic Properties + +| Property | Description | +|----------|-------------| +| `title` | Page title. Sets ``, `og:title`, and `twitter:title` | +| `description` | Page description for SEO. Sets meta description, `og:description`, and `twitter:description` | +| `keywords` | Keywords for SEO. Can be a string or array (arrays are joined with ", ") | +| `author` | Page author. Sets `meta[name="author"]` | +| `robots` | Robots directive (e.g., `"index, follow"`) | +| `lang` | HTML language attribute (e.g., `"en"`) | +| `canonical` | Canonical URL. Sets `<link rel="canonical">` | + +#### Open Graph Properties + +| Property | Description | Default | +|----------|-------------|---------| +| `image` | Social sharing image URL. Sets `og:image` and `twitter:image` | - | +| `url` | Canonical Open Graph URL. Sets `og:url` | - | +| `type` | Content type. Sets `og:type` | `"article"` | +| `siteName` | Site name. Sets `og:site_name` | - | +| `locale` | Locale (e.g., `"en_US"`). Sets `og:locale` | - | +| `siteUrl` | Base URL for resolving relative image paths | - | + +#### Twitter Card Properties + +| Property | Description | Default | +|----------|-------------|---------| +| `twitterCard` | Card type. Sets `twitter:card` | `"summary_large_image"` | +| `twitterSite` | Site's Twitter handle (e.g., `"@mysite"`). Sets `twitter:site` | - | +| `twitterCreator` | Author's Twitter handle. Sets `twitter:creator` | - | + +#### Article Properties + +| Property | Description | +|----------|-------------| +| `publishDate` | Publication date (ISO format). Sets `article:published_time` | +| `modifiedDate` | Last modified date (ISO format). Sets `article:modified_time` | +| `tags` | Article tags. Can be string or array. Sets `article:tag` | + +#### Example + +```yaml --- -title: My Page -description: A description for SEO -image: /og-image.png -keywords: ["react", "markdown"] -author: Your Name +title: "My Page Title" +description: "A brief description for SEO and social sharing" +keywords: ["react", "markdown", "static site"] +author: "Your Name" +image: "/og-image.png" +type: "article" +siteName: "My Site" +locale: "en_US" +twitterCard: "summary_large_image" +twitterSite: "@mysite" +publishDate: "2025-01-01" +tags: ["tutorial", "guide"] +canonical: "https://example.com/my-page" +lang: "en" +siteUrl: "https://example.com" --- ``` -These are automatically injected as HTML meta tags. +**Note:** Image URLs can be absolute (starting with `http://` or `https://`) or relative paths. Relative paths are resolved against `siteUrl` if provided. ### Using Components @@ -286,7 +352,7 @@ Scratch uses Tailwind CSS. Edit `src/tailwind.css` for global styles: ### Typography -Markdown content is styled with [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography). Use prose modifiers in `PageWrapper.jsx`: +Markdown content is styled with [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography). Use prose modifiers in `src/template/PageWrapper.jsx`: ```jsx <div className="prose prose-lg prose-slate"> @@ -308,7 +374,7 @@ Use the `not-prose` class to exclude elements from typography styling: ## PageWrapper -Create `src/PageWrapper.jsx` to wrap all pages with a layout: +Create `src/template/PageWrapper.jsx` to wrap all pages with a layout: ```jsx export default function PageWrapper({ children }) { diff --git a/pages/index.mdx b/pages/index.mdx index 17bb519..ce430e3 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -1,32 +1,43 @@ --- -title: "Scratch" -image: "/scratch-logo.svg" -description: "Write with Markdown and React" -keywords: ["MDX", "static site", "React", "Bun", "markdown"] -author: "Scratch" -type: "website" -lang: "en" +# Basic metadata +title: "Scratch" # Page title (<title>, og:title, twitter:title) +description: "Write with Markdown and React" # SEO description +keywords: ["MDX", "static site", "React", "Bun", "markdown"] # SEO keywords +author: "Pete Koomen" # Page author +robots: "index, follow" # Search engine directives +lang: "en" # HTML lang attribute +canonical: "https://scratch.dev/" # Canonical URL (<link rel="canonical">) + +# Open Graph (social sharing) +image: "https://scratch.dev/scratch-social.png" # Social sharing image (og:image, twitter:image) +url: "https://scratch.dev/" # Open Graph URL (og:url) +type: "website" # Content type (og:type) - "website" or "article" +siteName: "Scratch" # Site name (og:site_name) +locale: "en_US" # Locale (og:locale) +siteUrl: "https://scratch.dev" # Base URL for resolving relative image paths --- -<div className="not-prose flex flex-col items-center justify-center mb-8 mt-16"> - <img src="/scratch-logo.svg" alt="Scratch" height="120" /> -</div> +![Scratch logo](/scratch-logo.svg) -Scratch is a tool for writing with [Markdown](https://daringfireball.net/projects/markdown/) and [React](https://react.dev/). +Scratch is a tool for writing with [Markdown](https://daringfireball.net/projects/markdown/) and [React](https://react.dev/). -Write in Markdown and embed React components right in your text. Scratch compiles your work into beautiful static websites (like [this one](https://github.com/scratch/scratch.dev)) that can be hosted anywhere. +Write in Markdown and embed React components right in your text. Scratch compiles your work into beautiful static websites that can be hosted anywhere. -Scratch was designed for collaborative writing with coding agents like [Claude Code](https://www.claude.com/product/claude-code). Use your favorite editor to write in Markdown, and ask an agent for help when it's easier to express yourself with code. +Scratch was designed for collaborative writing with coding agents like [Claude Code](https://www.claude.com/product/claude-code). Use your favorite editor to write in Markdown and ask a coding agent for help when it's easier to express yourself with code. ## Quick Start -Scratch requires no configuration so it's easy to get started: +Scratch requires no configuration so it's easy to get started. First, install: ```bash # Install scratch curl -fsSL https://scratch.dev/install.sh | bash +``` + +Then create a project and start the dev server: +```bash # Create a new project scratch create @@ -34,7 +45,12 @@ scratch create scratch dev ``` -Now you're ready to start writing in `pages/index.mdx`. +Now you're ready to start writing in `pages/index.mdx`. When you're ready, use the `publish` command to share with specific people, your team, or the world: + +```bash +# Publish with a Scratch Server on Cloudflare +scratch publish +``` ## What can you do with Scratch? diff --git a/public/scratch-social.png b/public/scratch-social.png new file mode 100644 index 0000000..cc4205e Binary files /dev/null and b/public/scratch-social.png differ diff --git a/src/index.css b/src/tailwind.css similarity index 98% rename from src/index.css rename to src/tailwind.css index b081768..678c7b5 100644 --- a/src/index.css +++ b/src/tailwind.css @@ -18,7 +18,7 @@ /* H1 - center and add extra top margin */ .prose :where(h1):not(:where([class~='not-prose'] *)) { - @apply text-center mt-24; + @apply text-center mt-16; } /* Inline code - add background and padding, remove backticks */ diff --git a/src/template/Copyright.jsx b/src/template/Copyright.jsx new file mode 100644 index 0000000..131a290 --- /dev/null +++ b/src/template/Copyright.jsx @@ -0,0 +1,14 @@ +export default function Copyright({ name }) { + const author = name || (typeof window !== 'undefined' && window.__scratch_author__); + const year = new Date().getFullYear(); + + if (!author) { + return null; + } + + return ( + <p className="text-gray-400 text-sm"> + © {year} {author} + </p> + ); +} diff --git a/src/Footer.jsx b/src/template/Footer.jsx similarity index 66% rename from src/Footer.jsx rename to src/template/Footer.jsx index 84748da..c9055c1 100644 --- a/src/Footer.jsx +++ b/src/template/Footer.jsx @@ -1,12 +1,11 @@ import ScratchBadge from './ScratchBadge'; +import Copyright from './Copyright'; export default function Footer() { return ( <footer className="not-prose text-center mt-16 pb-8"> <ScratchBadge /> - <p className="text-gray-400 text-sm"> - MIT License · © 2025 Pete Koomen - </p> + <Copyright /> </footer> ); } diff --git a/src/Header.jsx b/src/template/Header.jsx similarity index 73% rename from src/Header.jsx rename to src/template/Header.jsx index a2c64d9..d6b3d2f 100644 --- a/src/Header.jsx +++ b/src/template/Header.jsx @@ -1,17 +1,17 @@ export default function Header() { return ( <header className="not-prose flex justify-between items-center mb-4"> - <a href="/" className="opacity-30 hover:opacity-100 transition-opacity"> + <a href="/" className="opacity-60 hover:opacity-100 transition-opacity"> <img src="/scratch-logo.svg" alt="Scratch" className="h-10" /> </a> <nav className="flex gap-6"> - <a href="/docs/" className="opacity-30 hover:opacity-100 transition-opacity">Docs</a> + <a href="/docs/" className="opacity-60 hover:opacity-100 transition-opacity">Docs</a> </nav> <div className="flex gap-4"> - <a href="https://github.com/scratch/scratch" target="_blank" rel="noopener noreferrer" className="opacity-30 hover:opacity-100 transition-opacity"> + <a href="https://github.com/scratch/scratch" target="_blank" rel="noopener noreferrer" className="opacity-60 hover:opacity-100 transition-opacity"> <img src="/github-mark.svg" alt="GitHub" className="w-6 h-6" /> </a> - <a href="https://x.com/koomen" target="_blank" rel="noopener noreferrer" className="opacity-30 hover:opacity-100 transition-opacity"> + <a href="https://x.com/koomen" target="_blank" rel="noopener noreferrer" className="opacity-60 hover:opacity-100 transition-opacity"> <img src="/x-logo.svg" alt="X" className="w-6 h-6" /> </a> </div> diff --git a/src/PageWrapper.jsx b/src/template/PageWrapper.jsx similarity index 87% rename from src/PageWrapper.jsx rename to src/template/PageWrapper.jsx index 40df948..d727feb 100644 --- a/src/PageWrapper.jsx +++ b/src/template/PageWrapper.jsx @@ -9,7 +9,7 @@ import Footer from './Footer'; */ export default function PageWrapper({ children }) { return ( - <div className="min-h-screen bg-white prose max-w-2xl mx-auto px-6 py-8"> + <div className="min-h-screen bg-white prose max-w-3xl mx-auto px-6 py-8"> <Header /> {children} <Footer /> diff --git a/src/template/ScratchBadge.jsx b/src/template/ScratchBadge.jsx new file mode 100644 index 0000000..e78c853 --- /dev/null +++ b/src/template/ScratchBadge.jsx @@ -0,0 +1,13 @@ +export default function ScratchBadge() { + return ( + <a + href="https://scratch.dev" + target="_blank" + rel="noopener noreferrer" + className="inline-flex items-center text-black text-base font-normal no-underline hover:no-underline" + > + <span className="text-lg">Made from</span> + <img src="/scratch-logo.svg" alt="Scratch" className="h-13 pb-1" /> + </a> + ); +}