From 76932822b8fff54e56812b7fc90450904ad276ae Mon Sep 17 00:00:00 2001
From: Drew Powers <1369770+drwpow@users.noreply.github.com>
Date: Tue, 20 Apr 2021 17:15:47 -0600
Subject: [PATCH] Split README into docs (#118)
---
README.md | 388 ++++++++------------------------------------
docs/api.md | 97 +++++++++++
docs/collections.md | 195 ++++++++++++++++++++++
docs/styling.md | 126 ++++++++++++++
4 files changed, 487 insertions(+), 319 deletions(-)
create mode 100644 docs/api.md
create mode 100644 docs/collections.md
create mode 100644 docs/styling.md
diff --git a/README.md b/README.md
index 415060afa6..73bd618e1a 100644
--- a/README.md
+++ b/README.md
@@ -55,14 +55,53 @@ export default {
};
```
+## 🥾 Guides
+
+### 🚀 Basic Usage
+
+Even though nearly-everything [is configurable][config], we recommend starting out by creating an `astro/` folder in your project with the following structure:
+
+```
+├── astro/
+│ ├── components/
+│ └── pages/
+│ └── index.astro
+├── public/
+└── package.json
+```
+
+- `astro/components/*`: where your reusable components go. You can place these anywhere, but we recommend a single folder to keep them organized.
+- `astro/pages/*`: this is a special folder where your [routing][routing] lives.
+
+#### 🚦 Routing
+
+Routing happens in `astro/pages/*`. Every `.astro` or `.md.astro` file in this folder corresponds with a public URL. For example:
+
+| Local file | Public URL |
+| :--------------------------------------- | :------------------------------ |
+| `astro/pages/index.astro` | `/index.html` |
+| `astro/pages/post/my-blog-post.md.astro` | `/post/my-blog-post/index.html` |
+
+#### 🗂 Static Assets
+
+Static assets should be placed in a `public/` folder in your project. You can place any images, fonts, files, or global CSS in here you need to reference.
+
+#### 🪨 Generating HTML with Astro
+
+TODO: Astro syntax guide
+
+#### ⚡ Dynamic Components
+
+TODO: Astro dynamic components guide
+
### 💧 Partial Hydration
By default, Astro outputs zero client-side JS. If you'd like to include an interactive component in the client output, you may use any of the following techniques.
- `` will render an HTML-only version of `MyComponent` (default)
- `` will render `MyComponent` on page load
-- `` will use [requestIdleCallback()][request-idle-cb] to render `MyComponent` as soon as main thread is free
-- `` will use an [IntersectionObserver][intersection-observer] to render `MyComponent` when the element enters the viewport
+- `` will use [requestIdleCallback()][mdn-ric] to render `MyComponent` as soon as main thread is free
+- `` will use an [IntersectionObserver][mdn-io] to render `MyComponent` when the element enters the viewport
### ⚛️ State Management
@@ -93,282 +132,40 @@ Styling in Astro is meant to be as flexible as you’d like it to be! The follow
¹ _`.astro` files have no runtime, therefore Scoped CSS takes the place of CSS Modules (styles are still scoped to components, but don’t need dynamic values)_
-#### 🖍 Styling by Framework
+To learn more about writing styles in Astro, see our [Styling Guide][docs-styling].
-##### Astro
+👉 [**Styling**][docs-styling]
-Styling in an Astro component is done by adding a `
-
-
I’m a scoped style and only apply to this component
-
I have both scoped and global styles
-```
-
-**Tips**
-
-- `
-```
-
-You should see Tailwind styles compile successfully in Astro.
-
-💁 **Tip**: to reduce duplication, try loading `@tailwind base` from a parent page (`./pages/*.astro`) instead of the component itself.
-
-## 🚀 Build & Deployment
+### 🚀 Build & Deployment
Add a `build` npm script to your `/package.json` file:
@@ -391,62 +188,15 @@ Now upload the contents of `/_site_` to your favorite static site host.
## 📚 API
-### `Astro` global
+👉 [**Full API Reference**][docs-api]
-The `Astro` global is available in all contexts in `.astro` files. It has the following functions:
-
-#### `config`
-
-`Astro.config` returns an object with the following properties:
-
-| Name | Type | Description |
-| :----- | :------- | :--------------------------------------------------------------------------------------------------------- |
-| `site` | `string` | Your website’s public root domain. Set it with `site: "https://mysite.com"` in your [Astro config][config] |
-
-#### `fetchContent()`
-
-`Astro.fetchContent()` is a way to load local `*.md` files into your static site setup. You can either use this on its own, or within [Astro Collections][collections].
-
-```
-// ./astro/components/my-component.astro
----
-const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of posts that live at ./astro/pages/post/*.md
----
-
-
-```
-
-`.fetchContent()` only takes one parameter: a relative URL glob of which local files you’d like to import. Currently only `*.md` files are supported. It’s synchronous, and returns an array of items of type:
-
-```
-{
- url: string; // the URL of this item (if it’s in pages/)
- content: string; // the HTML of this item
- // frontmatter data expanded here
-}[];
-```
-
-[autoprefixer]: https://github.com/postcss/autoprefixer
-[browserslist]: https://github.com/browserslist/browserslist
-[collections]: #-collections-beta
-[css-modules]: https://github.com/css-modules/css-modules
[config]: #%EF%B8%8F-configuration
-[fetch-content]: #fetchContent--
-[intersection-observer]: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
-[request-idle-cb]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
-[sass]: https://sass-lang.com/
-[sass-use]: https://sass-lang.com/documentation/at-rules/use
-[svelte]: https://svelte.dev
-[svelte-style]: https://svelte.dev/docs#style
-[tailwind]: https://tailwindcss.com
-[tailwind-utilities]: https://tailwindcss.com/docs/adding-new-utilities#using-css
-[vue-css-modules]: https://vue-loader.vuejs.org/guide/css-modules.html
-[vue-scoped]: https://vue-loader.vuejs.org/guide/scoped-css.html
+[docs-api]: ./docs/api.md
+[docs-collections]: ./docs/collections.md
+[docs-styling]: ./docs/styling.md
+[example-blog]: ./examples/blog
+[fetch-content]: ./docs/api.md#fetchcontent
+[fetch-js]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
+[mdn-io]: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
+[mdn-ric]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
+[routing]: #-routing
diff --git a/docs/api.md b/docs/api.md
new file mode 100644
index 0000000000..b1c106ef88
--- /dev/null
+++ b/docs/api.md
@@ -0,0 +1,97 @@
+## 📚 API
+
+### `Astro` global
+
+The `Astro` global is available in all contexts in `.astro` files. It has the following functions:
+
+#### `config`
+
+`Astro.config` returns an object with the following properties:
+
+| Name | Type | Description |
+| :----- | :------- | :--------------------------------------------------------------------------------------------------------- |
+| `site` | `string` | Your website’s public root domain. Set it with `site: "https://mysite.com"` in your [Astro config][config] |
+
+#### `fetchContent()`
+
+`Astro.fetchContent()` is a way to load local `*.md` files into your static site setup. You can either use this on its own, or within [Astro Collections][docs-collections].
+
+```jsx
+// ./astro/components/my-component.astro
+---
+const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of posts that live at ./astro/pages/post/*.md
+---
+
+
+```
+
+`.fetchContent()` only takes one parameter: a relative URL glob of which local files you’d like to import. Currently only `*.md` files are supported. It’s synchronous, and returns an array of items of type:
+
+```
+{
+ url: string; // the URL of this item (if it’s in pages/)
+ content: string; // the HTML of this item
+ // frontmatter data expanded here
+}[];
+```
+
+### `collection`
+
+```jsx
+export let collection;
+```
+
+When using the [Collections API][docs-collections], `collection` is a prop exposed to the page with the following shape:
+
+| Name | Type | Description |
+| :------------------------ | :-------------------: | :-------------------------------------------------------------------------------------------------------------------------------- |
+| `collection.data` | `Array` | Array of data returned from `data()` for the current page. |
+| `collection.start` | `number` | Index of first item on current page, starting at `0` (e.g. if `pageSize: 25`, this would be `0` on page 1, `25` on page 2, etc.). |
+| `collection.end` | `number` | Index of last item on current page. |
+| `collection.total` | `number` | The total number of items across all pages. |
+| `collection.page.current` | `number` | The current page number, starting with `1`. |
+| `collection.page.size` | `number` | How many items per-page. |
+| `collection.page.last` | `number` | The total number of pages. |
+| `collection.url.current` | `string` | Get the URL of the current page (useful for canonical URLs) |
+| `collection.url.prev` | `string \| undefined` | Get the URL of the previous page (will be `undefined` if on page 1). |
+| `collection.url.next` | `string \| undefined` | Get the URL of the next page (will be `undefined` if no more pages). |
+| `collection.params` | `object` | If page params were used, this returns a `{ key: value }` object of all values. |
+
+### `createCollection()`
+
+```jsx
+export async function createCollection() {
+ return {
+ async data({ params }) {
+ // load data
+ },
+ pageSize: 25,
+ routes: [{ tag: 'movie' }, { tag: 'television' }],
+ permalink: ({ params }) => `/tag/${params.tag}`,
+ };
+}
+```
+
+When using the [Collections API][docs-collections], `createCollection()` is an async function that returns an object of the following shape:
+
+| Name | Type | Description |
+| :---------- | :---------------------------: | :--------------------------------------------------------------------------------------------------------- |
+| `data` | `async ({ params }) => any[]` | **Required.** Load data with this function to be returned. |
+| `pageSize` | `number` | Specify number of items per page (default: `25`). |
+| `routes` | `params[]` | **Required for URL Params.** Return an array of all possible URL `param` values in `{ name: value }` form. |
+| `permalink` | `({ params }) => string` | **Required for URL Params.** Given a `param` object of `{ name: value }`, generate the final URL.\* |
+
+_\* Note: don’t create confusing URLs with `permalink`, e.g. rearranging params conditionally based on their values._
+
+⚠️ `createCollection()` executes in its own isolated scope before page loads. Therefore you can’t reference anything from its parent scope. If you need to load data you may fetch or use async `import()`s within the function body for anything you need (that’s why it’s `async`—to give you this ability). If it wasn’t isolated, then `collection` would be undefined! Therefore, duplicating imports between `createCollection()` and your Astro component is OK.
+
+[config]: ../README.md#%EF%B8%8F-configuration
+[docs-collections]: ./collections.md
diff --git a/docs/collections.md b/docs/collections.md
new file mode 100644
index 0000000000..f6599e3dc8
--- /dev/null
+++ b/docs/collections.md
@@ -0,0 +1,195 @@
+# 🍱 Collections
+
+## ❓ What are Collections?
+
+[Fetching data is easy in Astro][docs-data]. But what if you wanted to make a paginated blog? What if you wanted an easy way to sort data, or filter, say, by a given tag? When you need something a little more powerful than simple data fetching, Astro’s Collections API may be what you need.
+
+An Astro Collection is similar to the general concept of Collections in static site generators like Jekyll, Hugo, Eleventy, etc. It’s a general way to load an entire data set. But one big difference between Astro Collections and traditional static site generators is: **Astro lets you seamlessly blend remote API data and local files in a JAMstack-friendly way.** To see how, this guide will walk through a few examples. If you’d like, you can reference the [blog example project][example-blog] to see the finished code in context.
+
+## 🧑🎨 How to Use
+
+By default, any Astro component can fetch data from any API or local `*.md` files. But what if you had a blog you wanted to paginate? What if you wanted to generate dynamic URLs based on metadata (e.g. `/tag/:tag/`)? Or do both together? Astro Collections are a way to do all of that. It’s perfect for generating blog-like content, or scaffolding out dynamic URLs from your data.
+
+Let’s pretend we have some blog posts written already. This is our starting project structure:
+
+```
+└── astro/
+ └── pages/
+ └── post/
+ └── (blog content)
+```
+
+The first step in adding some dynamic collections is deciding on a URL schema. For our example website, we’re aiming for the following URLs:
+
+- `/post/:post`: A single blog post page
+- `/posts/:page`: A list page of all blog posts, paginated, and sorted most recent first
+- `/tag/:tag`: All blog posts, filtered by a specific tag
+
+Because `/post/:post` references the static files we have already, that doesn’t need to be a collection. But we will need collections for `/posts/:page` and `/tag/:tag` because those will be dynamically generated. For both collections we’ll create a `/astro/pages/$[collection].astro` file. This is our new structure:
+
+```diff
+ └── astro/
+ └── pages/
+ ├── post/
+ │ └── (blog content)
++ ├── $posts.astro -> /posts/1, /posts/2, …
++ └── $tag.astro -> /tag/:tag/1, /tag/:tag/2, …
+```
+
+💁 **Tip**: Any `.astro` filename beginning with a `$` is how it’s marked as a collection.
+
+In each `$[collection].astro` file, we’ll need 2 things:
+
+```js
+// 1. We need to mark “collection” as a prop (this is a special reserved name)
+export let collection: any;
+
+// 2. We need to export an async createCollection() function that will retrieve our data.
+export async function createCollection() {
+ return {
+ async data() {
+ // return data here to load (we’ll cover how later)
+ },
+ };
+}
+```
+
+These are important so your data is exposed to the page as a prop, and also Astro has everything it needs to gather your data and generate the proper routes. How it does this is more clear if we walk through a practical example.
+
+#### Example 1: Simple pagination
+
+Our blog posts all contain `title`, `tags`, and `published_at` in their frontmatter:
+
+```md
+---
+title: My Blog Post
+tags:
+ - javascript
+published_at: 2021-03-01 09:34:00
+---
+
+# My Blog post
+
+…
+```
+
+There’s nothing special or reserved about any of these names; you’re free to name everything whatever you’d like, or have as much or little frontmatter as you need.
+
+```jsx
+// /astro/pages/$posts.astro
+---
+export let collection: any;
+
+export async function createCollection() {
+ const allPosts = Astro.fetchContent('./post/*.md'); // load data that already lives at `/post/:slug`
+ allPosts.sort((a, b) => new Date(b.published_at) - new Date(a.published_at)); // sort newest -> oldest (we got "published_at" from frontmatter!)
+
+ // (load more data here, if needed)
+
+ return {
+ async data() {
+ return allPosts;
+ },
+ pageSize: 10, // how many we want to show per-page (default: 25)
+ };
+}
+
+function formatDate(date) {
+ return new Date(date).toUTCString();
+}
+---
+
+
+
+ Blog Posts: page {collection.page.current}
+
+
+
+
+
+
+
+
+
+
+```
+
+Let’s walk through some of the key parts:
+
+- `export let collection`: this is important because it exposes a prop to the page for Astro to return with all your data loaded. ⚠️ **It must be named `collection`**.
+- `export async function createCollection()`: this is also required, **and must be named this exactly.** This is an async function that lets you load data from anywhere (even a remote API!). At the end, you must return an object with `{ data: yourData }`. There are other options such as `pageSize` we’ll cover later.
+- `{collection.data.map((post) => (…`: this lets us iterate over all the markdown posts. This will take the shape of whatever you loaded in `createCollection()`. It will always be an array.
+- `{collection.page.current}`: this, and other properties, simply return more info such as what page a user is on, what the URL is, etc. etc.
+- Curious about everything on `collection`? See the [reference][collection-api].
+
+#### Example 2: Advanced filtering & pagination
+
+In our earlier example, we covered simple pagination for `/posts/1`, but we’d still like to make `/tag/:tag/1` and `/year/:year/1`. To do that, we’ll create 2 more collections: `/astro/pages/$tag.astro` and `astro/pages/$year.astro`. Assume that the markup is the same, but we’ve expanded the `createCollection()` function with more data.
+
+```diff
+ // /astro/pages/$tag.astro
+ ---
+ import Pagination from '../components/Pagination.astro';
+ import PostPreview from '../components/PostPreview.astro';
+
+ export let collection: any;
+
+ export async function createCollection() {
+ const allPosts = Astro.fetchContent('./post/*.md');
+ allPosts.sort((a, b) => new Date(b.published_at) - new Date(a.published_at));
++ const allTags = [...new Set(allPosts.map((post) => post.tags).flat())]; // gather all unique tags (we got "tags" from frontmatter!)
++ allTags.sort((a, b) => a.localeCompare(b)); // sort tags A -> Z
++ const routes = allTags.map((tag) => ({ tag })); // this is where we set { params: { tag } }
+
+ return {
+- async data() {
+- return allPosts;
++ async data({ params }) {
++ return allPosts.filter((post) => post.tags.includes(params.tag)); // filter posts that match the :tag from the URL ("params")
+ },
+ pageSize: 10,
++ routes,
++ permalink: ({ params }) => `/tag/${params.tag}/` // this is where we generate our URL structure
+ };
+ }
+ ---
+```
+
+Some important concepts here:
+
+- `routes = allTags.map((tag) => ({ tag }))`: Astro handles pagination for you automatically. But when it needs to generate multiple routes, this is where you tell Astro about all the possible routes. This way, when you run `astro build`, your static build isn’t missing any pages.
+- `permalink: ({ params }) => `/tag/${params.tag}/`: this is where you tell Astro what the generated URL should be. Note that while you have control over this, the root of this must match the filename (it’s best **NOT** to use `/pages/$tag.astro`to generate`/year/$year.astro`; that should live at `/pages/$year.astro` as a separate file).
+- `allPosts.filter((post) => post.tag === params.tag)`: we aren’t returning all posts here; we’re only returning posts with a matching tag. _What tag,_ you ask? The `routes` array has `[{ tag: 'javascript' }, { tag: '…`, and all the routes we need to gather. So we first need to query everything, but only return the `.filter()`ed posts at the very end.
+
+Other things of note is that we are sorting like before, but we filter by the frontmatter `tag` property, and return those at URLs.
+
+These are still paginated, too! But since there are other conditions applied, they live at a different URL.
+
+#### Tips
+
+- Having to load different collections in different `$[collection].astro` files might seem like a pain at first, until you remember **you can create reusable components!** Treat `/pages/*.astro` files as your one-off routing & data fetching logic, and treat `/components/*.astro` as your reusable markup. If you find yourself duplicating things too much, you can probably use a component instead!
+- Stay true to `/pages/$[collection].astro` naming. If you have an `/all-posts/*` route, then use `/pages/$all-posts.astro` to manage that. Don’t try and trick `permalink` to generate too many URL trees; it’ll only result in pages being missed when it comes time to build.
+
+### 📚 Further Reading
+
+- [Fetching data in Astro][docs-data]
+- API Reference: [collection][collection-api]
+- API Reference: [createCollection()][create-collection-api]
+
+[docs-data]: ../README.md#-fetching-data
+[collection-api]: ./api.md#collection
+[create-collection-api]: ./api.md#createcollection
+[example-blog]: ../examples/blog
+[fetch-content]: ./api.md#fetchcontent
diff --git a/docs/styling.md b/docs/styling.md
new file mode 100644
index 0000000000..e5546e887e
--- /dev/null
+++ b/docs/styling.md
@@ -0,0 +1,126 @@
+# 💅 Styling
+
+Styling in Astro is meant to be as flexible as you’d like it to be! The following options are all supported:
+
+| Framework | Global CSS | Scoped CSS | CSS Modules |
+| :--------------- | :--------: | :--------: | :---------: |
+| Astro (`.astro`) | ✅ | ✅ | N/A¹ |
+| React / Preact | ✅ | ❌ | ✅ |
+| Vue | ✅ | ✅ | ✅ |
+| Svelte | ✅ | ✅ | ❌ |
+
+¹ _`.astro` files have no runtime, therefore Scoped CSS takes the place of CSS Modules (styles are still scoped to components, but don’t need dynamic values)_
+
+#### 🖍 Styling by Framework
+
+##### Astro
+
+Styling in an Astro component is done by adding a `
+
+
I’m a scoped style and only apply to this component
+
I have both scoped and global styles
+```
+
+**Tips**
+
+- `
+```
+
+You should see Tailwind styles compile successfully in Astro.
+
+💁 **Tip**: to reduce duplication, try loading `@tailwind base` from a parent page (`./pages/*.astro`) instead of the component itself.
+
+[autoprefixer]: https://github.com/postcss/autoprefixer
+[browserslist]: https://github.com/browserslist/browserslist
+[css-modules]: https://github.com/css-modules/css-modules
+[vue-css-modules]: https://vue-loader.vuejs.org/guide/css-modules.html
+[vue-scoped]: https://vue-loader.vuejs.org/guide/scoped-css.html
+[sass]: https://sass-lang.com/
+[sass-use]: https://sass-lang.com/documentation/at-rules/use
+[svelte-style]: https://svelte.dev/docs#style
+[tailwind]: https://tailwindcss.com
+[tailwind-utilities]: https://tailwindcss.com/docs/adding-new-utilities#using-css