@ -28,7 +28,7 @@ const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of po
</div>
</div>
```
```
`.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:
`.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:
```js
```js
{
{
@ -113,9 +113,9 @@ When using the [Collections API](/core-concepts/collections), `createCollection(
| `permalink` | `({ params }) => string` | **Required for URL Params.** Given a `param` object of `{ name: value }`, generate the final URL.\* |
| `permalink` | `({ params }) => string` | **Required for URL Params.** Given a `param` object of `{ name: value }`, generate the final URL.\* |
| `rss` | [RSS](/reference/api-reference#rss-feed) | Optional: generate an RSS 2.0 feed from this collection ([docs](/reference/api-reference#rss-feed)) |
| `rss` | [RSS](/reference/api-reference#rss-feed) | Optional: generate an RSS 2.0 feed from this collection ([docs](/reference/api-reference#rss-feed)) |
_\* Note: don’t create confusing URLs with `permalink`, e.g. rearranging params conditionally based on their values._
_\* 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.
⚠️ `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.
#### RSS Feed
#### RSS Feed
@ -143,7 +143,7 @@ export async function createCollection() {
item: (item) => ({
item: (item) => ({
title: item.title,
title: item.title,
description: item.description,
description: item.description,
pubDate: item.pubDate + 'Z', // enforce GMT timezone (otherwise it’ll be different based on where it’s built)
pubDate: item.pubDate + 'Z', // enforce GMT timezone (otherwise it'll be different based on where it's built)
/** (optional) add arbitrary XML to each <item> */
/** (optional) add arbitrary XML to each <item> */
@ -30,7 +30,7 @@ In the summer of 2020, he managed to formulated his thoughts of how web architec
His seminal post outlines and discusses the general concept of 'islands' as an architectural design process that could be used in Web Development, allowing for better improvements in overall site performance, SEO, UX, and everywhere else. His given explanation describing this new paradigm, was extraordinarily succinct:
His seminal post outlines and discusses the general concept of 'islands' as an architectural design process that could be used in Web Development, allowing for better improvements in overall site performance, SEO, UX, and everywhere else. His given explanation describing this new paradigm, was extraordinarily succinct:
> "The general idea of an _“Islands”_ architecture is deceptively simple: Render HTML pages on the server, and inject placeholders or slots around highly dynamic regions. These placeholders/slots contain the server-rendered HTML output from their corresponding widget. They denote regions that can then be "hydrated" on the client into small self-contained widgets, reusing their server-rendered initial HTML."-Jason Miller
> "The general idea of an _"Islands"_ architecture is deceptively simple: Render HTML pages on the server, and inject placeholders or slots around highly dynamic regions. These placeholders/slots contain the server-rendered HTML output from their corresponding widget. They denote regions that can then be "hydrated" on the client into small self-contained widgets, reusing their server-rendered initial HTML."-Jason Miller
To develop a better understanding of what Jason meant with his proposal, let's quickly explore the backdrop, before we explain 'Island Architecture' and how it is applied into Astro as our primary ethos.
To develop a better understanding of what Jason meant with his proposal, let's quickly explore the backdrop, before we explain 'Island Architecture' and how it is applied into Astro as our primary ethos.
@ -34,7 +34,7 @@ An Astro component represents some snippet of HTML in your project. This can be
CSS rules inside of a `<style>` tag are automatically scoped to that component. That means that you can reuse class names across multiple components, without worrying about conflicts. Styles are automatically extracted and optimized in the final build so that you don't need to worry about style loading.
CSS rules inside of a `<style>` tag are automatically scoped to that component. That means that you can reuse class names across multiple components, without worrying about conflicts. Styles are automatically extracted and optimized in the final build so that you don't need to worry about style loading.
For best results, you should only have one `<style>` tag per-Astro component. This isn’t necessarily a limitation, but it will often result in better-optimized CSS in your final build. When you're working with pages, the `<style>` tag can go nested inside of your page `<head>`. For standalone components, the `<style>` tag can go at the top-level of your template.
For best results, you should only have one `<style>` tag per-Astro component. This isn't necessarily a limitation, but it will often result in better-optimized CSS in your final build. When you're working with pages, the `<style>` tag can go nested inside of your page `<head>`. For standalone components, the `<style>` tag can go at the top-level of your template.
It’s important to note that Astro **won’t** transform HTML references for you. For example, consider an `<img>` tag with a relative `src` attribute inside `src/pages/about.astro`:
It's important to note that Astro **won't** transform HTML references for you. For example, consider an `<img>` tag with a relative `src` attribute inside `src/pages/about.astro`:
```html
```html
<!-- ❌ Incorrect: will try and load `/about/thumbnail.png` -->
<!-- ❌ Incorrect: will try and load `/about/thumbnail.png` -->
@ -325,6 +325,6 @@ import thumbnailSrc from './thumbnail.png';
<imgsrc={thumbnailSrc}/>
<imgsrc={thumbnailSrc}/>
```
```
If you’d prefer to organize assets alongside Astro components, you may import the file in JavaScript inside the component script. This works as intended but this makes `thumbnail.png` harder to reference in other parts of your app, as its final URL isn’t easily-predictable (unlike assets in `public/*`, where the final URL is guaranteed to never change).
If you'd prefer to organize assets alongside Astro components, you may import the file in JavaScript inside the component script. This works as intended but this makes `thumbnail.png` harder to reference in other parts of your app, as its final URL isn't easily-predictable (unlike assets in `public/*`, where the final URL is guaranteed to never change).
@ -258,7 +258,7 @@ export async function createCollection() {
item: (item) => ({
item: (item) => ({
title: item.title,
title: item.title,
description: item.description,
description: item.description,
// enforce GMT timezone (otherwise it’ll be different based on where it’s built)
// enforce GMT timezone (otherwise it'll be different based on where it's built)
pubDate: item.pubDate + 'Z',
pubDate: item.pubDate + 'Z',
// custom data is supported here as well
// custom data is supported here as well
}),
}),
@ -269,7 +269,7 @@ export async function createCollection() {
Astro will generate your RSS feed at the URL `/feed/[collection].xml`. For example, `/src/pages/$podcast.astro` would generate URL `/feed/podcast.xml`.
Astro will generate your RSS feed at the URL `/feed/[collection].xml`. For example, `/src/pages/$podcast.astro` would generate URL `/feed/podcast.xml`.
Even though Astro will create the RSS feed for you, you’ll still need to add `<link>` tags manually in your `<head>` HTML for feed readers and browsers to pick up:
Even though Astro will create the RSS feed for you, you'll still need to add `<link>` tags manually in your `<head>` HTML for feed readers and browsers to pick up:
@ -75,9 +75,9 @@ Styling in Astro is meant to be as flexible as you’d like it to be! The follow
| `.vue` | ✅ | ✅ | ✅ |
| `.vue` | ✅ | ✅ | ✅ |
| `.svelte` | ✅ | ✅ | ❌ |
| `.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)_
¹ _`.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)_
All styles in Astro are automatically [**autoprefixed**](#cross-browser-compatibility), minified and bundled, so you can just write CSS and we’ll handle the rest ✨.
All styles in Astro are automatically [**autoprefixed**](#cross-browser-compatibility), minified and bundled, so you can just write CSS and we'll handle the rest ✨.
---
---
@ -112,7 +112,7 @@ Astro also supports [Sass][sass] out-of-the-box. To enable for each framework:
- **Vue**: `<style lang="scss">` or `<style lang="sass">`
- **Vue**: `<style lang="scss">` or `<style lang="sass">`
- **Svelte**: `<style lang="scss">` or `<style lang="sass">`
- **Svelte**: `<style lang="scss">` or `<style lang="sass">`
💁 Sass is great! If you haven’t used Sass in a while, please give it another try. The new and improved [Sass Modules][sass-use] are a great fit with modern web development, and it’s blazing-fast since being rewritten in Dart. And the best part? **You know it already!** Use `.scss` to write familiar CSS syntax you’re used to, and only sprinkle in Sass features if/when you need them.
💁 Sass is great! If you haven't used Sass in a while, please give it another try. The new and improved [Sass Modules][sass-use] are a great fit with modern web development, and it's blazing-fast since being rewritten in Dart. And the best part? **You know it already!** Use `.scss` to write familiar CSS syntax you're used to, and only sprinkle in Sass features if/when you need them.
### 🍃 Tailwind
### 🍃 Tailwind
@ -144,7 +144,7 @@ Be sure to add the config path to `astro.config.mjs`, so that Astro enables JIT
};
};
```
```
Now you’re ready to write Tailwind! Our recommended approach is to create a `public/global.css` file (or whatever you‘d like to name your global stylesheet) with [Tailwind utilities][tailwind-utilities] like so:
Now you're ready to write Tailwind! Our recommended approach is to create a `public/global.css` file (or whatever you‘d like to name your global stylesheet) with [Tailwind utilities][tailwind-utilities] like so:
```css
```css
/* public/global.css */
/* public/global.css */
@ -153,7 +153,7 @@ Now you’re ready to write Tailwind! Our recommended approach is to create a `p
@tailwind utilities;
@tailwind utilities;
```
```
As an alternative to `public/global.css`, You may also add Tailwind utilities to individual `pages/*.astro` components in `<style>` tags, but be mindful of duplication! If you end up creating multiple Tailwind-managed stylesheets for your site, make sure you’re not sending the same CSS to users over and over again in separate CSS files.
As an alternative to `public/global.css`, You may also add Tailwind utilities to individual `pages/*.astro` components in `<style>` tags, but be mindful of duplication! If you end up creating multiple Tailwind-managed stylesheets for your site, make sure you're not sending the same CSS to users over and over again in separate CSS files.
### Importing from npm
### Importing from npm
@ -170,21 +170,21 @@ If you want to import third-party libraries into an Astro component, you can use
All CSS is minified and bundled automatically for you in running `astro build`. Without getting too in the weeds, the general rules are:
All CSS is minified and bundled automatically for you in running `astro build`. Without getting too in the weeds, the general rules are:
- If a style only appears on one route, it’s only loaded for that route (`/_astro/[page]-[hash].css`)
- If a style only appears on one route, it's only loaded for that route (`/_astro/[page]-[hash].css`)
- If a style appears on multiple routes, it’s deduplicated into a `/_astro/common-[hash].css` bundle
- If a style appears on multiple routes, it's deduplicated into a `/_astro/common-[hash].css` bundle
- All styles are hashed according to their contents (the hashes only change if the contents do!)
- All styles are hashed according to their contents (the hashes only change if the contents do!)
We’ll be expanding our styling optimization story over time, and would love your feedback! If `astro build` generates unexpected styles, or if you can think of improvements, [please open an issue][issues].
We'll be expanding our styling optimization story over time, and would love your feedback! If `astro build` generates unexpected styles, or if you can think of improvements, [please open an issue][issues].
_Note: be mindful when some page styles get extracted to the ”common” bundle, and some page styles stay on-page. For most people this may not pose an issue, but when part of your styles are bundled they technically may load in a different order and your cascade may be different. While this problem isn’t unique to Astro and is present in almost any CSS bundling process, it can be unexpected if you’re not anticipating it. Be sure to inspect your final production build, and please [report any issues][issues] you may come across._
_Note: be mindful when some page styles get extracted to the "common" bundle, and some page styles stay on-page. For most people this may not pose an issue, but when part of your styles are bundled they technically may load in a different order and your cascade may be different. While this problem isn't unique to Astro and is present in almost any CSS bundling process, it can be unexpected if you're not anticipating it. Be sure to inspect your final production build, and please [report any issues][issues] you may come across._
## Advanced Styling Architecture
## Advanced Styling Architecture
Too many development setups take a hands-off approach to CSS, or at most leave you with only contrived examples that don’t get you very far. Telling developers “Use whatever styling solution you want!” is a nice thought that rarely works out in practice. Few styling approaches lend themselves to every setup. Astro is no different—certain styling approaches _will_ work better than others.
Too many development setups take a hands-off approach to CSS, or at most leave you with only contrived examples that don't get you very far. Telling developers "Use whatever styling solution you want!" is a nice thought that rarely works out in practice. Few styling approaches lend themselves to every setup. Astro is no different—certain styling approaches _will_ work better than others.
An example to illustrate this: Astro removes runtime JS (even the core framework if possible). Thus, depending on Styled Components for all your styles would be bad, as that would require React to load on pages where it’s not needed. Or at best, you’d get a “[FOUC][fouc]” as your static HTML is served but the user waits for JavaScript to download and execute. Or consider a second example at the opposite end of the spectrum: _BEM_. You _can_ use a completely-decoupled [BEM][bem] or [SMACSS][smacss] approach in Astro. But that’s a lot of manual maintenance you can avoid, and it leaves out a lof of convenience of [Astro components](/core-concepts/astro-components).
An example to illustrate this: Astro removes runtime JS (even the core framework if possible). Thus, depending on Styled Components for all your styles would be bad, as that would require React to load on pages where it's not needed. Or at best, you'd get a "[FOUC][fouc]" as your static HTML is served but the user waits for JavaScript to download and execute. Or consider a second example at the opposite end of the spectrum: _BEM_. You _can_ use a completely-decoupled [BEM][bem] or [SMACSS][smacss] approach in Astro. But that's a lot of manual maintenance you can avoid, and it leaves out a lof of convenience of [Astro components](/core-concepts/astro-components).
We think there’s a great middle ground between intuitive-but-slow CSS-in-JS and fast-but-cumbersome global CSS: **Hybrid Scoped + Utility CSS**. This approach works well in Astro, is performant for users, and will be the best styling solution in Astro _for most people_ (provided you’re willing to learn a little). So as a quick recap:
We think there's a great middle ground between intuitive-but-slow CSS-in-JS and fast-but-cumbersome global CSS: **Hybrid Scoped + Utility CSS**. This approach works well in Astro, is performant for users, and will be the best styling solution in Astro _for most people_ (provided you're willing to learn a little). So as a quick recap:
**This approach is good for…**
**This approach is good for…**
@ -195,13 +195,13 @@ We think there’s a great middle ground between intuitive-but-slow CSS-in-JS an
- Developers that already have strong opinions on styling, and want to control everything themselves
- Developers that already have strong opinions on styling, and want to control everything themselves
Read on if you’re looking for some strong opinions 🙂. We’ll describe the approach by enforcing a few key rules that should govern how you set your styles:
Read on if you're looking for some strong opinions 🙂. We'll describe the approach by enforcing a few key rules that should govern how you set your styles:
### Hybrid Scoped + Utility CSS
### Hybrid Scoped + Utility CSS
#### Scoped styles
#### Scoped styles
You don’t need an explanation on component-based design. You already know that reusing components is a good idea. And it’s this idea that got people used to concepts like [Styled Components][styled-components] and [Styled JSX][styled-jsx]. But rather than burden your users with slow load times of CSS-in-JS, Astro has something better: **built-in scoped styles.**
You don't need an explanation on component-based design. You already know that reusing components is a good idea. And it's this idea that got people used to concepts like [Styled Components][styled-components] and [Styled JSX][styled-jsx]. But rather than burden your users with slow load times of CSS-in-JS, Astro has something better: **built-in scoped styles.**
```jsx
```jsx
---
---
@ -220,9 +220,9 @@ You don’t need an explanation on component-based design. You already know that
</button>
</button>
```
```
_Note: all the examples here use `lang="scss"` which is a great convenience for nesting, and sharing [colors and variables][sass-use], but it’s entirely optional and you may use normal CSS if you wish._
_Note: all the examples here use `lang="scss"` which is a great convenience for nesting, and sharing [colors and variables][sass-use], but it's entirely optional and you may use normal CSS if you wish._
That `.btn` class is scoped within that component, and won’t leak out. It means that you can **focus on styling and not naming.** Local-first approach fits in very well with Astro’s ESM-powered design, favoring encapsulation and reusability over global scope. While this is a simple example, it should be noted that **this scales incredibly well.** And if you need to share common values between components, [Sass’ module system][sass-use] also gets our recommendation for being easy to use, and a great fit with component-first design.
That `.btn` class is scoped within that component, and won't leak out. It means that you can **focus on styling and not naming.** Local-first approach fits in very well with Astro's ESM-powered design, favoring encapsulation and reusability over global scope. While this is a simple example, it should be noted that **this scales incredibly well.** And if you need to share common values between components, [Sass' module system][sass-use] also gets our recommendation for being easy to use, and a great fit with component-first design.
By contrast, Astro does allow global styles via the `:global()` and `<style global>` escape hatches. However, this should be avoided if possible. To illustrate this: say you used your button in a `<Nav />` component, and you wanted to style it differently there. You might be tempted to have something like:
By contrast, Astro does allow global styles via the `:global()` and `<style global>` escape hatches. However, this should be avoided if possible. To illustrate this: say you used your button in a `<Nav />` component, and you wanted to style it differently there. You might be tempted to have something like:
@ -234,7 +234,7 @@ import Button from './Button.astro';
<stylelang="scss">
<stylelang="scss">
.nav :global(.btn) {
.nav :global(.btn) {
/* ❌ This will fight with <Button>’s styles */
/* ❌ This will fight with <Button>'s styles */
}
}
</style>
</style>
@ -243,7 +243,7 @@ import Button from './Button.astro';
</nav>
</nav>
```
```
This is undesirable because now `<Nav>` and `<Button>` fight over what the final button looks like. Now, whenever you edit one, you’ll always have to edit the other, and they are no longer truly isolated as they once were (now coupled by a bidirectional styling dependency). It’s easy to see how this pattern only has to repeated a couple times before being afraid that touching any styles _anywhere_ may break styling in a completely different part of the app (queue `peter-griffin-css-blinds.gif`).
This is undesirable because now `<Nav>` and `<Button>` fight over what the final button looks like. Now, whenever you edit one, you'll always have to edit the other, and they are no longer truly isolated as they once were (now coupled by a bidirectional styling dependency). It's easy to see how this pattern only has to repeated a couple times before being afraid that touching any styles _anywhere_ may break styling in a completely different part of the app (queue `peter-griffin-css-blinds.gif`).
Instead, let `<Button>` control its own styles, and try a prop:
Instead, let `<Button>` control its own styles, and try a prop:
Elsewhere, you can use `<Button theme="nav">` to set the type of button it is. This preserves the contract of _Button is in charge of its styles, and Nav is in charge of its styles_, and now you can edit one without affecting the other. The worst case scenario of using global styles is that the component is broken and unusable (it’s missing part of its core styles). But the worst case scenario of using props (e.g. typo) is that a component will only fall back to its default, but still usable, state.
Elsewhere, you can use `<Button theme="nav">` to set the type of button it is. This preserves the contract of _Button is in charge of its styles, and Nav is in charge of its styles_, and now you can edit one without affecting the other. The worst case scenario of using global styles is that the component is broken and unusable (it's missing part of its core styles). But the worst case scenario of using props (e.g. typo) is that a component will only fall back to its default, but still usable, state.
💁 **Why this works well in Astro**: Astro is inspired most by JavaScript modules: you only need to know about what’s in one file at a time, and you never have to worry about something in a remote file affecting how this code runs. But we’re not alone in this; Vue and Svelte have both capitalized on and popularized the idea that styles and markup are natural fits in the same component file. [You can still have separation of concerns][peace-on-css] even with markup, styling, and logic contained in one file. In fact, that’s what makes component design so powerful! So write CSS without fear that you picked a name that’s used by some other component across your app.
💁 **Why this works well in Astro**: Astro is inspired most by JavaScript modules: you only need to know about what's in one file at a time, and you never have to worry about something in a remote file affecting how this code runs. But we're not alone in this; Vue and Svelte have both capitalized on and popularized the idea that styles and markup are natural fits in the same component file. [You can still have separation of concerns][peace-on-css] even with markup, styling, and logic contained in one file. In fact, that's what makes component design so powerful! So write CSS without fear that you picked a name that's used by some other component across your app.
#### Utility CSS
#### Utility CSS
Recently there has been a debate of all-scoped component styles vs utility-only CSS. But we agree with people like Sarah Dayan who ask [why can’t we have both][utility-css]? Truth is that while having scoped component styles are great, there are still hundreds of times when the website’s coming together when two components just don’t line up _quite_ right, and one needs a nudge. Or different text treatment is needed in one component instance.
Recently there has been a debate of all-scoped component styles vs utility-only CSS. But we agree with people like Sarah Dayan who ask [why can't we have both][utility-css]? Truth is that while having scoped component styles are great, there are still hundreds of times when the website's coming together when two components just don't line up _quite_ right, and one needs a nudge. Or different text treatment is needed in one component instance.
While the thought of having perfect, pristine components is nice, it’s unrealistic. No design system is absoutely perfect, and every design system has inconsistencies. And it’s in reconciling these inconsistencies where components can become a mess without utility CSS. Utility CSS is great for adding minor tweaks necessary to get the website out the door. But they also are incomplete on their own—if you’ve ever tried to manage responsive styles or accessible focus states with utility CSS it can quickly become a mess! **Utility CSS works best in partnership with component (scoped) CSS**. And in order to be as easy as possible to use, Utility CSS should be global (arguably should be your only global CSS, besides maybe reset.css) so you don’t have to deal with imports all willy-nilly.
While the thought of having perfect, pristine components is nice, it's unrealistic. No design system is absoutely perfect, and every design system has inconsistencies. And it's in reconciling these inconsistencies where components can become a mess without utility CSS. Utility CSS is great for adding minor tweaks necessary to get the website out the door. But they also are incomplete on their own—if you've ever tried to manage responsive styles or accessible focus states with utility CSS it can quickly become a mess! **Utility CSS works best in partnership with component (scoped) CSS**. And in order to be as easy as possible to use, Utility CSS should be global (arguably should be your only global CSS, besides maybe reset.css) so you don't have to deal with imports all willy-nilly.
Some great problems best handled with Utility CSS are:
Some great problems best handled with Utility CSS are:
@ -292,7 +292,7 @@ In Astro, we recommend the following setup for this:
</head>
</head>
```
```
And in your local filesystem, you can even use Sass’ [@use][sass-use] to combine files together effortlessly:
And in your local filesystem, you can even use Sass' [@use][sass-use] to combine files together effortlessly:
```
```
├── public/
├── public/
@ -306,15 +306,15 @@ And in your local filesystem, you can even use Sass’ [@use][sass-use] to combi
└── (pages)
└── (pages)
```
```
What’s in each file is up to you to determine, but start small, add utilities as you need them, and you’ll keep your CSS weight incredibly low. And utilities you wrote to meet your real needs will always be better than anything off the shelf.
What's in each file is up to you to determine, but start small, add utilities as you need them, and you'll keep your CSS weight incredibly low. And utilities you wrote to meet your real needs will always be better than anything off the shelf.
So to recap, think of scoped styles as the backbone of your styles that get you 80% of the way there, and utility CSS filling in the remaining 20%. They both work well in tandem, with each compensating for the other’s weakness.
So to recap, think of scoped styles as the backbone of your styles that get you 80% of the way there, and utility CSS filling in the remaining 20%. They both work well in tandem, with each compensating for the other's weakness.
💁 **Why this works well in Astro**: Astro was built around the idea of **Scoped CSS and Global Utility CSS living together in harmony** ♥️! Take full advantage of it.
💁 **Why this works well in Astro**: Astro was built around the idea of **Scoped CSS and Global Utility CSS living together in harmony** ♥️! Take full advantage of it.
### More suggestions
### More suggestions
”But wait!” you may ask, having read the previous section. ”That doesn’t take care of [my usecase]!” If you‘re looking for more pointers on some common styling problems, you may be interested in the following suggestions. These all are cohesive, and fit with the **Hybrid Scoped + Utility** philosophy:
"But wait!" you may ask, having read the previous section. "That doesn't take care of [my usecase]!" If you‘re looking for more pointers on some common styling problems, you may be interested in the following suggestions. These all are cohesive, and fit with the **Hybrid Scoped + Utility** philosophy:
1. Split your app into Layout Components and Base Components
1. Split your app into Layout Components and Base Components
1. Avoid Flexbox and Grid libraries (write your own!)
1. Avoid Flexbox and Grid libraries (write your own!)
@ -323,7 +323,7 @@ So to recap, think of scoped styles as the backbone of your styles that get you
#### Suggestion #1: Split your app into Layout Components and Base Components
#### Suggestion #1: Split your app into Layout Components and Base Components
While this guide will never be long enough to answer the question _”How should a page be laid out?”_ (that’s a [design problem!][cassie-evans-css]) there is a more specific question hiding within that we _can_ answer: _“Given a layout, how should components/styles be organized?”_ The answer is **don’t bake layout into components.** Have layout components that control layout, and base components (buttons, cards, etc.) that don’t control layout. _What does that mean?_ Let’s walk through an example so it’s more clear. Pretend we have a page that looks like this (numbers for different components):
While this guide will never be long enough to answer the question _"How should a page be laid out?"_ (that's a [design problem!][cassie-evans-css]) there is a more specific question hiding within that we _can_ answer: _"Given a layout, how should components/styles be organized?"_ The answer is **don't bake layout into components.** Have layout components that control layout, and base components (buttons, cards, etc.) that don't control layout. _What does that mean?_ Let's walk through an example so it's more clear. Pretend we have a page that looks like this (numbers for different components):
```
```
|---------------|
|---------------|
@ -337,7 +337,7 @@ While this guide will never be long enough to answer the question _”How should
|---+---+---+---|
|---+---+---+---|
```
```
The layout consists of a big, giant, full-width post at top, followed by two half-width posts below it. And below that, we want a bunch of smaller posts to fill out the rest of the page. For simplicity, we’ll just call these `<BigPost>` (1), `<MediumPost>` (2), and `<SmallPost>` (3). We add them to our page like so:
The layout consists of a big, giant, full-width post at top, followed by two half-width posts below it. And below that, we want a bunch of smaller posts to fill out the rest of the page. For simplicity, we'll just call these `<BigPost>` (1), `<MediumPost>` (2), and `<SmallPost>` (3). We add them to our page like so:
```jsx
```jsx
---
---
@ -365,7 +365,7 @@ import Footer from '../components/Footer.astro';
</html>
</html>
```
```
This _looks_ clean, but looks can be deceiving. At first glance, we may think that `<Grid>` is controlling the layout, but that’s an illusion. We actually have `<BigPost>` handling its own width, `<MediumPosts>` loading 2 components and controlling its width, and `<SmallPosts>` loading 4+ components and controlling its width. In total, including `<Grid>`, that means **4 components** are all fighting over the same layout. Remove one post from `<MediumPosts>`, the layout breaks. Edit `<BigPost>`, the layout breaks. Edit `<Grid>`, the layout breaks. If you think about it, none of these components are truly reusable—they might as well just be one big file.
This _looks_ clean, but looks can be deceiving. At first glance, we may think that `<Grid>` is controlling the layout, but that's an illusion. We actually have `<BigPost>` handling its own width, `<MediumPosts>` loading 2 components and controlling its width, and `<SmallPosts>` loading 4+ components and controlling its width. In total, including `<Grid>`, that means **4 components** are all fighting over the same layout. Remove one post from `<MediumPosts>`, the layout breaks. Edit `<BigPost>`, the layout breaks. Edit `<Grid>`, the layout breaks. If you think about it, none of these components are truly reusable—they might as well just be one big file.
This is actually the **Global CSS Problem** in disguise—multiple components fight over how they all lay out together, without layout being one, central responsibility (kinda like global CSS)! Now that we identified the problem, one way to fix this is to hoist the entire layout to the top level, and load all components there, too:
This is actually the **Global CSS Problem** in disguise—multiple components fight over how they all lay out together, without layout being one, central responsibility (kinda like global CSS)! Now that we identified the problem, one way to fix this is to hoist the entire layout to the top level, and load all components there, too:
@ -436,9 +436,9 @@ import Footer from '../components/Footer.astro';
</html>
</html>
```
```
Getting over that this is more code, it’s actually a much cleaner separation. What was a four-component layout is now managed 100% within the top-level `index.astro` (which we can now consider a **Layout Component**, and if we wanted to reuse this we could extract this into its own file). Your layout is centralized, and now these components truly are reusable because they don’t care one bit about whether they’re in the same grid or not. You can edit styles in any of these files now without fear of styles breaking in another.
Getting over that this is more code, it's actually a much cleaner separation. What was a four-component layout is now managed 100% within the top-level `index.astro` (which we can now consider a **Layout Component**, and if we wanted to reuse this we could extract this into its own file). Your layout is centralized, and now these components truly are reusable because they don't care one bit about whether they're in the same grid or not. You can edit styles in any of these files now without fear of styles breaking in another.
The basic rule is that when orchestrating multiple components, **that’s a unique responsibility** that should live in one central place, rather than split between 4 components as we were doing. In fact, top-level pages are great at this, and should always be the starting point of your layout components. See how far you can take it, and only extract layout components when you absolutely have to.
The basic rule is that when orchestrating multiple components, **that's a unique responsibility** that should live in one central place, rather than split between 4 components as we were doing. In fact, top-level pages are great at this, and should always be the starting point of your layout components. See how far you can take it, and only extract layout components when you absolutely have to.
To recap: **if you have to touch multiple files to manage one layout, you probably need to reorganize everything into a Layout Component.**
To recap: **if you have to touch multiple files to manage one layout, you probably need to reorganize everything into a Layout Component.**
@ -446,7 +446,7 @@ To recap: **if you have to touch multiple files to manage one layout, you probab
#### Suggestion #2: Avoid Flexbox and Grid libraries (write your own!)
#### Suggestion #2: Avoid Flexbox and Grid libraries (write your own!)
This may feel like a complete overreach to tell you not to use your favorite layout framework you’re familiar with. After all, it’s gotten you this far! But the days of [float madness](https://zellwk.com/blog/responsive-grid-system/) are gone, replaced by Flexbox and Grid. And the latter don’t need libraries to manage them (often they can make it harder).
This may feel like a complete overreach to tell you not to use your favorite layout framework you're familiar with. After all, it's gotten you this far! But the days of [float madness](https://zellwk.com/blog/responsive-grid-system/) are gone, replaced by Flexbox and Grid. And the latter don't need libraries to manage them (often they can make it harder).
Many front-end developers experience the following train of thought:
Many front-end developers experience the following train of thought:
@ -454,26 +454,26 @@ Many front-end developers experience the following train of thought:
2. Many pages reuse the same layout, … (_hold up—_)
2. Many pages reuse the same layout, … (_hold up—_)
3. … therefore I can find an existing solution to manage all my duplicate layouts (_wait a minute—_)
3. … therefore I can find an existing solution to manage all my duplicate layouts (_wait a minute—_)
While the logic is sound, the reality is that #2 isn’t truth for many projects. Probably, many parts of the website weren’t designed to fit into these nice, neat, 12 column grids. Even modest web apps can contain _hundreds_ of unique layouts when you factor in all the breakpoints. Ask yourself: _If the website I’m building really contains so many unique layouts, why am I using a heavy grid library that only gives me generic layouts?_
While the logic is sound, the reality is that #2 isn't truth for many projects. Probably, many parts of the website weren't designed to fit into these nice, neat, 12 column grids. Even modest web apps can contain _hundreds_ of unique layouts when you factor in all the breakpoints. Ask yourself: _If the website I'm building really contains so many unique layouts, why am I using a heavy grid library that only gives me generic layouts?_
A few well-written lines of CSS Grid here and there will not only be perfect in every occasion; it’s likely lighter and easier to manage than that heavy library you’ve fought with for so long. Another way to look at it: if you have to spend a couple hours learning a proprietary styling framework, wrestling with it, filing issues, etc., why not just spend that time on Flexbox and Grid instead? For many people, learning the basics only takes an hour, and that can get you pretty far! There are great, free, learning resources that are worth your time:
A few well-written lines of CSS Grid here and there will not only be perfect in every occasion; it's likely lighter and easier to manage than that heavy library you've fought with for so long. Another way to look at it: if you have to spend a couple hours learning a proprietary styling framework, wrestling with it, filing issues, etc., why not just spend that time on Flexbox and Grid instead? For many people, learning the basics only takes an hour, and that can get you pretty far! There are great, free, learning resources that are worth your time:
- [Flexbox Froggy](https://flexboxfroggy.com/)
- [Flexbox Froggy](https://flexboxfroggy.com/)
- [CSS Grid Garden](https://cssgridgarden.com/)
- [CSS Grid Garden](https://cssgridgarden.com/)
So in short: stop trying to deduplicate layouts when there’s nothing to deduplicate! You’ll find your styles not only easier to manage, but your CSS payloads much lighter, and load times faster.
So in short: stop trying to deduplicate layouts when there's nothing to deduplicate! You'll find your styles not only easier to manage, but your CSS payloads much lighter, and load times faster.
💁 **Why this works well in Astro**: grid libraries are a quick path to stylesheet bloat, and a major contributor to people attempting to [treeshake their styles][css-treeshaking]. Astro does **not** treeshake unused CSS for you, because [that can cause problems][css-treeshaking]. We’re not saying you have to be library free; we’re big fans of libraries like [Material UI][material-ui]. But if you can at least shed the thousands upon thousands of layouts you’re not using from your styling library, you probably don’t need automatic treeshaking.
💁 **Why this works well in Astro**: grid libraries are a quick path to stylesheet bloat, and a major contributor to people attempting to [treeshake their styles][css-treeshaking]. Astro does **not** treeshake unused CSS for you, because [that can cause problems][css-treeshaking]. We're not saying you have to be library free; we're big fans of libraries like [Material UI][material-ui]. But if you can at least shed the thousands upon thousands of layouts you're not using from your styling library, you probably don't need automatic treeshaking.
#### Suggestion #3: Avoid `margin` on a component wrapper
#### Suggestion #3: Avoid `margin` on a component wrapper
In other words, don’t do this:
In other words, don't do this:
```jsx
```jsx
<!-- src/components/MyComponent.astro -->
<!-- src/components/MyComponent.astro -->
<stylelang="scss">
<stylelang="scss">
.wrapper {
.wrapper {
/* ❌ Don’t do this! */
/* ❌ Don't do this! */
margin-top: 3rem;
margin-top: 3rem;
}
}
</style>
</style>
@ -481,27 +481,27 @@ In other words, don’t do this:
<divclass="wrapper"></div>
<divclass="wrapper"></div>
```
```
If you remember the [CSS box model][box-model], `margin` extends beyond the boundaries of the box. This means that when you place `margin` on the outermost element, now that will push other components next to it. Even though the styles are scoped, it’s _technically_ affecting elements around it, so it [breaks the concept of style containment][layout-isolated].
If you remember the [CSS box model][box-model], `margin` extends beyond the boundaries of the box. This means that when you place `margin` on the outermost element, now that will push other components next to it. Even though the styles are scoped, it's _technically_ affecting elements around it, so it [breaks the concept of style containment][layout-isolated].
When you have components that rearrange, or appear different when they’re next to other components, that’s a hard battle to win. **Components should look and act the same no matter where they are placed.** That’s what makes them components!
When you have components that rearrange, or appear different when they're next to other components, that's a hard battle to win. **Components should look and act the same no matter where they are placed.** That's what makes them components!
💁 **Why this works well in Astro**: margins pushing other components around creeps into your styling architecture in sneaky ways, and can result in the creation of some wonky or brittle layout components. Avoiding it altogether will keep your layout components simpler, and you’ll spend less time styling in general.
💁 **Why this works well in Astro**: margins pushing other components around creeps into your styling architecture in sneaky ways, and can result in the creation of some wonky or brittle layout components. Avoiding it altogether will keep your layout components simpler, and you'll spend less time styling in general.
#### Suggestion #4: Avoid global media queries
#### Suggestion #4: Avoid global media queries
The final point is a natural boundary of **Scoped Styles**. That extends to breakpoints, too! You know that one, weird breakpoint where your `<Card />` component wraps awkwardly at a certain size? You should handle that within `<Card />`, and not anywhere else.
The final point is a natural boundary of **Scoped Styles**. That extends to breakpoints, too! You know that one, weird breakpoint where your `<Card />` component wraps awkwardly at a certain size? You should handle that within `<Card />`, and not anywhere else.
Even if you end up with some random value like `@media (min-width: 732px) {`, that’ll probably work better than trying to create a global [magic number][magic-number] somewhere that only applies to one context (an arbitrary value may be “magic” to the rest of an app, but it does still have meaning within the context of a component that needs that specific value).
Even if you end up with some random value like `@media (min-width: 732px) {`, that'll probably work better than trying to create a global [magic number][magic-number] somewhere that only applies to one context (an arbitrary value may be "magic" to the rest of an app, but it does still have meaning within the context of a component that needs that specific value).
Granted, this has been near-impossible to achieve until Container Queries; fortunately [they are finally landing!][container-queries]
Granted, this has been near-impossible to achieve until Container Queries; fortunately [they are finally landing!][container-queries]
Also, a common complaint of this approach is when someone asks _”What if I have 2 components that need to do the same thing at the same breakpoint?”_ to which my answer is: you’ll always have one or two of those; just handle those as edge cases. But if your entire app is made up of dozens of these cases, perhaps your component lines could be redrawn so that they’re more [layout-isolated][layout-isolated] in general.
Also, a common complaint of this approach is when someone asks _"What if I have 2 components that need to do the same thing at the same breakpoint?"_ to which my answer is: you'll always have one or two of those; just handle those as edge cases. But if your entire app is made up of dozens of these cases, perhaps your component lines could be redrawn so that they're more [layout-isolated][layout-isolated] in general.
💁 **Why this works well in Astro**: this is probably the least important point, which is why it’s saved for last. In fact, you could probably skip this if it doesn’t work for you. But it’s something that people try to architect for at scale, and having a global system to manage this can often be unnecessary. Give _not_ architecting for global media queries a try, and see how far it takes you!
💁 **Why this works well in Astro**: this is probably the least important point, which is why it's saved for last. In fact, you could probably skip this if it doesn't work for you. But it's something that people try to architect for at scale, and having a global system to manage this can often be unnecessary. Give _not_ architecting for global media queries a try, and see how far it takes you!
### 👓 Further Reading
### 👓 Further Reading
This guide wouldn’t be possible without the following blog posts, which expand on these topics and explain them in more detail. Please give them a read!
This guide wouldn't be possible without the following blog posts, which expand on these topics and explain them in more detail. Please give them a read!
- [**Layout-isolated Components**][layout-isolated] by Emil Sjölander
- [**Layout-isolated Components**][layout-isolated] by Emil Sjölander
- [**In defense of utility-first CSS**][utility-css] by Sarah Dayan
- [**In defense of utility-first CSS**][utility-css] by Sarah Dayan
@ -28,7 +28,7 @@ const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of po
</div>
</div>
```
```
`.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:
`.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:
```js
```js
{
{
@ -81,7 +81,7 @@ export async function createCollection() {
<!-- Your HTML template here. -->
<!-- Your HTML template here. -->
```
```
⚠️ The `createCollection()` function executes in its own isolated scope before page loads. Therefore you can’t reference anything from its parent scope, other than file imports. The compiler will warn if you break this requirement.
⚠️ The `createCollection()` function executes in its own isolated scope before page loads. Therefore you can't reference anything from its parent scope, other than file imports. The compiler will warn if you break this requirement.
The `createCollection()` function should returns an object of the following shape:
The `createCollection()` function should returns an object of the following shape: