diff options
Diffstat (limited to 'docs/src/pages/guides')
-rw-r--r-- | docs/src/pages/guides/data-fetching.md | 64 | ||||
-rw-r--r-- | docs/src/pages/guides/deploy.md | 256 | ||||
-rw-r--r-- | docs/src/pages/guides/imports.md | 139 | ||||
-rw-r--r-- | docs/src/pages/guides/markdown-content.md | 222 | ||||
-rw-r--r-- | docs/src/pages/guides/publish-to-npm.md | 83 | ||||
-rw-r--r-- | docs/src/pages/guides/styling.md | 511 |
6 files changed, 1275 insertions, 0 deletions
diff --git a/docs/src/pages/guides/data-fetching.md b/docs/src/pages/guides/data-fetching.md new file mode 100644 index 000000000..4f972dd82 --- /dev/null +++ b/docs/src/pages/guides/data-fetching.md @@ -0,0 +1,64 @@ +--- +layout: ~/layouts/Main.astro +title: Data Fetching +--- + +Astro components and pages can fetch remote data to help generate your pages. Astro provides two different tools to pages to help you do this: **fetch()** and **top-level await.** + +## `fetch()` + +Astro pages have access to the global `fetch()` function in their setup script. `fetch()` is a native JavaScript API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)) that lets you make HTTP requests for things like APIs and resources. + +Even though Astro component scripts run inside of Node.js (and not in the browser) Astro provides this native API so that you can fetch data at page build time. + +```astro +--- +// Movies.astro +const response = await fetch('https://example.com/movies.json'); +const data = await response.json(); +// Remember: Astro component scripts log to the CLI +console.log(data); +--- +<!-- Output the result to the page --> +<div>{JSON.stringify(data)}</div> +``` + +## Top-level await + +`await` is another native JavaScript feature that lets you await the response of some asynchronous promise ([MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)). Astro supports `await` in the top-level of your component script. + +**Important:** These are not yet available inside of non-page Astro components. Instead, do all of your data loading inside of your pages, and then pass them to your components as props. + +## Using `fetch()` outside of Astro Components + +If you want to use `fetch()` in a non-astro component, use the [`node-fetch`](https://github.com/node-fetch/node-fetch) library: + +```tsx +// Movies.tsx +import fetch from 'node-fetch'; +import type { FunctionalComponent } from 'preact'; +import { h } from 'preact'; + +const data = fetch('https://example.com/movies.json').then((response) => + response.json() +); + +// Components that are build-time rendered also log to the CLI. +// If you loaded this component with a directive, it would log to the browser console. +console.log(data); + +const Movies: FunctionalComponent = () => { + // Output the result to the page + return <div>{JSON.stringify(data)}</div>; +}; + +export default Movies; +``` + +If you load a component using `node-fetch` [interactively](/core-concepts/component-hydration), with `client:load`, `client:visible`, etc., you'll need to either not use `node-fetch` or switch to an [isomorphic](https://en.wikipedia.org/wiki/Isomorphic_JavaScript) library that will run both at build time and on the client, as the [`node-fetch` README.md](https://github.com/node-fetch/node-fetch#motivation) reccomends: + +> Instead of implementing XMLHttpRequest in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native http to fetch API directly? Hence, node-fetch, minimal code for a window.fetch compatible API on Node.js runtime. +> +> See Jason Miller's [isomorphic-unfetch](https://www.npmjs.com/package/isomorphic-unfetch) or Leonardo Quixada's [cross-fetch](https://github.com/lquixada/cross-fetch) for isomorphic usage (exports node-fetch for server-side, whatwg-fetch for client-side). + +> Quoted from https://github.com/node-fetch/node-fetch#motivation diff --git a/docs/src/pages/guides/deploy.md b/docs/src/pages/guides/deploy.md new file mode 100644 index 000000000..9bca1d56d --- /dev/null +++ b/docs/src/pages/guides/deploy.md @@ -0,0 +1,256 @@ +--- +layout: ~/layouts/Main.astro +title: Deploy a Website +--- + +> This page is based off of [Vite's](https://vitejs.dev/) well-documented [static deploy instructions](https://vitejs.dev/guide/static-deploy.html). + +The following guides are based on some shared assumptions: + +- You are using the default build output location (`dist/`). This location [can be changed using the `dist` configuration option](/reference/configuration-reference). +- You are using npm. You can use equivalent commands to run the scripts if you are using Yarn or other package managers. +- Astro is installed as a local dev dependency in your project, and you have setup the following npm scripts: + +```json +{ + "scripts": { + "start": "astro dev", + "build": "astro build" + } +} +``` + + + +## Building The App + +You may run `npm run build` command to build the app. + +```bash +$ npm run build +``` + +By default, the build output will be placed at `dist/`. You may deploy this `dist/` folder to any of your preferred platforms. + +## GitHub Pages + +1. Set the correct `buildOptions.site` in `astro.config.js`. +2. Run `touch public/.nojekyll` to create a `.nojekyll` file in `public/`. This [bypasses GitHub Page's default behavior](https://github.blog/2009-12-29-bypassing-jekyll-on-github-pages/) to ignore paths prefixed with `_`. +3. Inside your project, create `deploy.sh` with the following content (uncommenting the appropriate lines), and run it to deploy: + + ```bash{13,20,23} + #!/usr/bin/env sh + + # abort on errors + set -e + + # build + npm run build + + # navigate into the build output directory + cd dist + + # if you are deploying to a custom domain + # echo 'www.example.com' > CNAME + + git init + git add -A + git commit -m 'deploy' + + # if you are deploying to https://<USERNAME>.github.io + # git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git main + + # if you are deploying to https://<USERNAME>.github.io/<REPO> + # git push -f git@github.com:<USERNAME>/<REPO>.git main:gh-pages + + cd - + ``` +> You can also run the above script in your CI setup to enable automatic deployment on each push. + +### GitHub Actions + +TODO: We'd love an example action snippet to share here! + +### Travis CI + +1. Set the correct `buildOptions.site` in `astro.config.js`. +2. Create a file named `.travis.yml` in the root of your project. +3. Run `npm install` locally and commit the generated lockfile (`package-lock.json`). +4. Use the GitHub Pages deploy provider template, and follow the [Travis CI documentation](https://docs.travis-ci.com/user/deployment/pages/). + + ```yaml + language: node_js + node_js: + - lts/* + install: + - npm ci + script: + - npm run build + deploy: + provider: pages + skip_cleanup: true + local_dir: dist + # A token generated on GitHub allowing Travis to push code on you repository. + # Set in the Travis settings page of your repository, as a secure variable. + github_token: $GITHUB_TOKEN + keep_history: true + on: + branch: master + ``` + +## GitLab Pages + +1. Set the correct `buildOptions.site` in `astro.config.js`. +2. Set `build.outDir` in `astro.config.js` to `public`. +3. Create a file called `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content: + + ```yaml + image: node:10.22.0 + pages: + cache: + paths: + - node_modules/ + script: + - npm install + - npm run build + artifacts: + paths: + - public + only: + - master + ``` + +## Netlify + +In your codebase, make sure you have a `.nvmrc` file with `v14.15.1` in it. + +You can configure your deploy in two ways, via the Netlify website or with the `netlify.toml` file. + +With the `netlify.toml` file, add it at the top level of your project with the following settings: + +```toml +[build] + command = "npm run build" + publish = "dist" +``` + +Then, set up a new project on [Netlify](https://netlify.com) from your chosen Git provider. + +If you don't want to use the `netlify.toml`, when you go to [Netlify](https://netlify.com) and set up up a new project from Git, input the following settings: + + - **Build Command:** `astro build` or `npm run build` + - **Publish directory:** `dist` + +Then hit the deploy button. + +## Google Firebase + +1. Make sure you have [firebase-tools](https://www.npmjs.com/package/firebase-tools) installed. + +2. Create `firebase.json` and `.firebaserc` at the root of your project with the following content: + + `firebase.json`: + + ```json + { + "hosting": { + "public": "dist", + "ignore": [] + } + } + ``` + + `.firebaserc`: + + ```js + { + "projects": { + "default": "<YOUR_FIREBASE_ID>" + } + } + ``` + +3. After running `npm run build`, deploy using the command `firebase deploy`. + +## Surge + +1. First install [surge](https://www.npmjs.com/package/surge), if you haven’t already. + +2. Run `npm run build`. + +3. Deploy to surge by typing `surge dist`. + +You can also deploy to a [custom domain](http://surge.sh/help/adding-a-custom-domain) by adding `surge dist yourdomain.com`. + +## Heroku + +1. Install [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). + +2. Create a Heroku account by [signing up](https://signup.heroku.com). + +3. Run `heroku login` and fill in your Heroku credentials: + + ```bash + $ heroku login + ``` + +4. Create a file called `static.json` in the root of your project with the below content: + + `static.json`: + + ```json + { + "root": "./dist" + } + ``` + + This is the configuration of your site; read more at [heroku-buildpack-static](https://github.com/heroku/heroku-buildpack-static). + +5. Set up your Heroku git remote: + + ```bash + # version change + $ git init + $ git add . + $ git commit -m "My site ready for deployment." + + # creates a new app with a specified name + $ heroku apps:create example + + # set buildpack for static sites + $ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git + ``` + +6. Deploy your site: + + ```bash + # publish site + $ git push heroku master + + # opens a browser to view the Dashboard version of Heroku CI + $ heroku open + ``` + +## Vercel + +To deploy your Astro project with a [Vercel for Git](https://vercel.com/docs/git), make sure it has been pushed to a Git repository. + +Go to https://vercel.com/import/git and import the project into Vercel using your Git of choice (GitHub, GitLab or BitBucket). Follow the wizard to select the project root with the project's `package.json` and override the build step using `npm run build` and the output dir to be `./dist` + +After your project has been imported, all subsequent pushes to branches will generate Preview Deployments, and all changes made to the Production Branch (commonly "main") will result in a Production Deployment. + +Once deployed, you will get a URL to see your app live, such as the following: https://astro.vercel.app + +## Azure Static Web Apps + +You can deploy your Astro project with Microsoft Azure [Static Web Apps](https://aka.ms/staticwebapps) service. You need: + +- An Azure account and a subscription key. You can create a [free Azure account here](https://azure.microsoft.com/free). +- Your app code pushed to [GitHub](https://github.com). +- The [SWA Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps) in [Visual Studio Code](https://code.visualstudio.com). + +Install the extension in VS Code and navigate to your app root. Open the Static Web Apps extension, sign in to Azure, and click the '+' sign to create a new Static Web App. You will be prompted to designate which subscription key to use. + +Follow the wizard started by the extension to give your app a name, choose a framework preset, and designate the app root (usually `/`) and built file location `/dist`. The wizard will run and will create a GitHub action in your repo in a `.github` folder. + +The action will work to deploy your app (watch its progress in your repo's Actions tab) and, when successfully completed, you can view your app in the address provided in the extension's progress window by clicking the 'Browse Website' button that appears when the GitHub action has run. diff --git a/docs/src/pages/guides/imports.md b/docs/src/pages/guides/imports.md new file mode 100644 index 000000000..595f13e62 --- /dev/null +++ b/docs/src/pages/guides/imports.md @@ -0,0 +1,139 @@ +--- +layout: ~/layouts/Main.astro +title: Supported Imports +--- + +Astro uses Snowpack as its internal build system. Snowpack provides Astro with built-in support for the following file types, with no configuration required: + +- JavaScript (`.js`, `.mjs`) +- TypeScript (`.ts`, `.tsx`) +- JSON (`.json`) +- JSX (`.jsx`, `.tsx`) +- CSS (`.css`) +- CSS Modules (`.module.css`) +- Images & Assets (`.svg`, `.jpg`, `.png`, etc.) +- Astro Components (`.astro`) +- Markdown (`.md`) +- WASM (`.wasm`) + +Any files in your `public/` directory are copied into the final build, untouched by Snowpack or Astro. The following applies to files in your `src/` directory, which Astro is ultimately responsible for. + +## JavaScript & ESM + +Astro was designed for JavaScript's native ES Module (ESM) syntax. ESM lets you define explicit imports & exports that browsers and build tools can better understand and optimize for. If you're familiar with the `import` and `export` keywords in JavaScript, then you already know ESM! + +```js +// ESM Example - src/user.js +export function getUser() { + /* ... */ +} + +// src/index.js +import {getUser} from './user.js'; +``` + +All browsers now support ESM, so Astro is able to ship this code directly to the browser during development. + +## TypeScript + +Astro includes built-in support to build TypeScript files (`*.ts`) to JavaScript. Astro components also support TypeScript in the frontmatter script section. + +Note that this built-in support is build only. By default, Astro does not type-check your TypeScript code. + +<!-- To integrate type checking into your development/build workflow, add the [@snowpack/plugin-typescript](https://www.npmjs.com/package/@snowpack/plugin-typescript) plugin. --> + +## JSX + +Astro includes built-in support to build JSX files (`*.jsx` & `*.tsx`) to JavaScript. + +If you are using Preact, Astro will detect your Preact import and switch to use the Preact-style JSX `h()` function. This is all done automatically for you. + +**Note: Astro does not support JSX in `.js`/`.ts` files.** + +## JSON + +```js +// Load the JSON object via the default export +import json from './data.json'; +``` + +Astro supports importing JSON files directly into your application. Imported files return the full JSON object in the default import. + +## CSS + +```js +// Load and inject 'style.css' onto the page +import './style.css'; +``` + +Astro supports importing CSS files directly into your application. Imported styles expose no exports, but importing one will automatically add those styles to the page. This works for all CSS files by default, and can support compile-to-CSS languages like Sass & Less via plugins. + +If you prefer not to write CSS, Astro also supports all popular CSS-in-JS libraries (ex: styled-components) for styling. + +## CSS Modules + +```js +// 1. Converts './style.module.css' classnames to unique, scoped values. +// 2. Returns an object mapping the original classnames to their final, scoped value. +import styles from './style.module.css'; + +// This example uses JSX, but you can use CSS Modules with any framework. +return <div className={styles.error}>Your Error Message</div>; +``` + +Astro supports CSS Modules using the `[name].module.css` naming convention. Like any CSS file, importing one will automatically apply that CSS to the page. However, CSS Modules export a special default `styles` object that maps your original classnames to unique identifiers. + +CSS Modules help you enforce component scoping & isolation on the frontend with unique-generated class names for your stylesheets. + +## Other Assets + +```jsx +import imgReference from './image.png'; // img === '/src/image.png' +import svgReference from './image.svg'; // svg === '/src/image.svg' +import txtReference from './words.txt'; // txt === '/src/words.txt' + +// This example uses JSX, but you can use import references with any framework. +<img src={imgReference} />; +``` + +All other assets not explicitly mentioned above can be imported via ESM `import` and will return a URL reference to the final built asset. This can be useful for referencing non-JS assets by URL, like creating an image element with a `src` attribute pointing to that image. + +## WASM + +```js +// Loads and intializes the requested WASM file +const wasm = await WebAssembly.instantiateStreaming(fetch('/example.wasm')); +``` + +Astro supports loading WASM files directly into your application using the browser's [`WebAssembly`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly) API. Read our [WASM guide](/guides/wasm) to learn more. + +## NPM Packages + +```js +// Returns the React & React-DOM npm packages +import React from 'react'; +import ReactDOM from 'react-dom'; +``` + +Astro lets you import npm packages directly in the browser. Even if a package was published using a legacy format, Astro will up-convert the package to ESM before serving it to the browser. + +When you start up your dev server or run a new build, you may see a message that Snowpack is "installing dependencies". This means that Snowpack is converting your dependencies to run in the browser. This needs to run only once, or until you next change your dependency tree by adding or removing dependencies. + +## Node Builtins + +We encourage Astro users to avoid Node.js builtins (`fs`, `path`, etc) whenever possible. Astro aims to be compatible with multiple JavaScript runtimes in the future. This includes [Deno](https://deno.land/) and [Cloudflare Workers](https://workers.cloudflare.com/) which do not support Node builtin modules such as `fs`. + +Our aim is to provide Astro alternatives to common Node.js builtins. However, no such alternatives exist today. So, if you _really_ need to use these builtin modules we don't want to stop you. Astro supports Node.js builtins using Node's newer `node:` prefix. If you want to read a file, for example, you can do so like this: + +```jsx +--- +// Example: import the "fs/promises" builtin from Node.js +import fs from 'node:fs/promises'; + +const url = new URL('../../package.json', import.meta.url); +const json = await fs.readFile(url, 'utf-8'); +const data = JSON.parse(json); +--- + +<span>Version: {data.version}</span> +``` diff --git a/docs/src/pages/guides/markdown-content.md b/docs/src/pages/guides/markdown-content.md new file mode 100644 index 000000000..1314e9ea8 --- /dev/null +++ b/docs/src/pages/guides/markdown-content.md @@ -0,0 +1,222 @@ +--- +layout: ~/layouts/Main.astro +title: Markdown +--- + +Astro comes with out-of-the-box Markdown support powered by the expansive [remark](https://remark.js.org/) ecosystem. + +## Remark and Rehype Plugins + +In addition to custom components inside the [`<Markdown>` component](/guides/markdown-content#markdown-component), Astro comes with [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants), and syntax highlighting via [Prism](https://prismjs.com/) pre-enabled. + +Also, Astro supports third-party plugins for Markdown. You can provide your plugins in `astro.config.mjs`. + +> **Note:** Enabling custom `remarkPlugins` or `rehypePlugins` removes Astro's built-in support for [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants). You must explicitly add these plugins to your `astro.config.mjs` file, if desired. + +## Add a markdown plugin in Astro + +If you want to add a plugin, you need to install the npm package dependency in your project and then update the `markdownOptions.remarkPlugins` or `markdownOptions.rehypePlugins` depends on what plugin you want to have: + +```js +// astro.config.js +export default { + markdownOptions: { + remarkPlugins: [ + // Add a Remark plugin that you want to enable for your project. + // If you need to provide options for the plugin, you can use an array and put the options as the second item. + // 'remark-slug', + // ['remark-autolink-headings', { behavior: 'prepend'}], + ] + rehypePlugins: [ + // Add a Rehype plugin that you want to enable for your project. + // If you need to provide options for the plugin, you can use an array and put the options as the second item. + // 'rehype-slug', + // ['rehype-autolink-headings', { behavior: 'prepend'}], + ] + }, +}; +``` + +You can provide names of the plugins as well as import them: + +```js +// astro.config.js +export default { + markdownOptions: { + remarkPlugins: [import('remark-slug'), [import('remark-autolink-headings'), { behavior: 'prepend' }]], + }, +}; +``` + +### Markdown Pages + +Astro treats any `.md` files inside of the `/src/pages` directory as pages. These pages are processed as plain markdown files and do not support components. If you're looking to embed rich components in your markdown, take a look at the [Markdown Component](#astros-markdown-component) section. + +`layout` + +The only special Frontmatter key is `layout`, which defines the relative path to an `.astro` component which should wrap your Markdown content. + +`src/pages/index.md` + +```jsx +--- +layout: ../layouts/main.astro +--- + +# Hello World! +``` + +Layout files are normal `.astro` components. Any Frontmatter defined in your `.md` page will be exposed to the Layout component as the `content` prop. `content` also has an `astro` key which holds special metadata about your file, like the complete Markdown `source` and a `headings` object. + +Keep in mind that the only guaranteed variables coming from the `content` prop object are `astro` and `url`. An example of what a blog post `content` object might look like is as follows: + +```json +{ + /** Frontmatter from blog post + "title": "", + "date": "", + "author": "", + "description": "", + **/ + "astro": { + "headers": [], + "source": "" + }, + "url": "" +} +``` + +The rendered Markdown content is placed into the default `<slot />` element. + +`src/layouts/main.astro` + +```jsx +--- +const { content } = Astro.props; +--- + +<html> + <head> + <title>{content.title}</title> + </head> + + <body> + <slot/> + </body> +</html> +``` + +### Astro's Markdown Component + +Astro has a dedicated component used to let you render your markdown as HTML components. This is a special component that is only exposed to `.astro` files. To use the `<Markdown>` component, within yout frontmatter block use the following import statement: + +```jsx +--- +import { Markdown } from 'astro/components'; +--- +``` + +You can utilize this within your `.astro` file by doing the following: + +```jsx +--- +import { Markdown } from 'astro/components'; +--- + +<Layout> + <Markdown> + # Hello world! + + The contents inside here is all in markdown. + </Markdown> +</Layout> +``` + +`<Markdown>` components provide more flexibility and allow you to use plain HTML or custom components. For example: + +````jsx +--- +// For now, this import _must_ be named "Markdown" and _must not_ be wrapped with a custom component +// We're working on easing these restrictions! +import { Markdown } from 'astro/components'; +import Layout from '../layouts/main.astro'; +import MyFancyCodePreview from '../components/MyFancyCodePreview.tsx'; + +const expressions = 'Lorem ipsum'; +--- + +<Layout> + <Markdown> + # Hello world! + + **Everything** supported in a `.md` file is also supported here! + + There is _zero_ runtime overhead. + + In addition, Astro supports: + - Astro {expressions} + - Automatic indentation normalization + - Automatic escaping of expressions inside code blocks + + ```jsx + // This content is not transformed! + const object = { someOtherValue }; + ``` + + - Rich component support like any `.astro` file! + - Recursive Markdown support (Component children are also processed as Markdown) + + <MyFancyCodePreview client:visible> + ```jsx + const object = { someOtherValue }; + ``` + </MyFancyCodePreview client:visible> + </Markdown> +</Layout> +```` + +### Remote Markdown + +If you have Markdown in a remote source, you may pass it directly to the Markdown component through the `content` attribute. For example, the example below fetches the README from Snowpack's Github repository and renders it as HTML. + +```jsx +--- +import { Markdown } from 'astro/components'; + +const content = await fetch('https://raw.githubusercontent.com/snowpackjs/snowpack/main/README.md').then(res => res.text()); +--- + +<Layout> + <Markdown content={content} /> +</Layout> +``` + +There might be times when you want to combine both dynamic, and static markdown. If that is the case, you can nest `<Markdown>` components with each other to get the best of both worlds. + +```jsx +--- +import { Markdown } from 'astro/components'; + +const content = await fetch('https://raw.githubusercontent.com/snowpackjs/snowpack/main/README.md').then(res => res.text()); +--- + +<Layout> + <Markdown> + ## Markdown example + + Here we have some __Markdown__ code. We can also dynamically render content from remote places. + + <Markdown content={content} /> + </Mardown> +</Layout> +``` + +### Security FAQs + +**Aren't there security concerns to rendering remote markdown directly to HTML?** + +Yes! Just like with regular HTML, improper use of the `Markdown` component can open you up to a [cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) attack. If you are rendering untrusted content, be sure to _sanitize your content **before** rendering it_. + +**Why not use a prop like React's `dangerouslySetInnerHTML={{ __html: content }}`?** + +Rendering a string of HTML (or Markdown) is an extremely common use case when rendering a static site and you probably don't need the extra hoops to jump through. Rendering untrusted content is always dangerous! Be sure to _sanitize your content **before** rendering it_. diff --git a/docs/src/pages/guides/publish-to-npm.md b/docs/src/pages/guides/publish-to-npm.md new file mode 100644 index 000000000..aebf0311a --- /dev/null +++ b/docs/src/pages/guides/publish-to-npm.md @@ -0,0 +1,83 @@ +--- +layout: ~/layouts/Main.astro +title: Publish a Component to NPM +--- + +Built a great Astro component? **Publish it to [npm!](https://npmjs.com/)** + +Once published to npm, Astro components can be installed and used in your project like any other npm package. npm is a great way to share Astro components across projects within your team, your company, or the entire world. + +## Basic NPM Package Setup + +Here's an example package that we'd like to publish to npm. It includes two Astro components and a few other files. + +``` +/my-components-package/ +├── package.json +├── index.js +├── Capitalize.astro +└── Bold.astro +``` + +### `package.json` + +Your package manifest. This includes information about your package such as name, description, any dependencies, and other important metadata. If you don't know what the `package.json` file is, we highly recommend you to have a quick read on [the npm documentation](https://docs.npmjs.com/creating-a-package-json-file). + +We recommend that you define an [exports entry](https://nodejs.org/api/packages.html) for your `index.js` package entrypoint like so: + +```json +{ + "name": "@example/my-components", + "version": "0.0.1", + "exports": "./index.js" +} +``` + +### `index.js` + +`index.js` is your package entrypoint, which is the file that gets loaded when someone imports your package by name. Having a JavaScript file as your package entrypoint will let you export multiple components and have better control over their exported component names. + +```js +export { default as Capitalize } from './Capitalize.astro'; +export { default as Bold } from './Bold.astro'; +``` + +### Publishing + +Once you have your package ready, you can publish it to npm by running the command `npm publish`. If that fails, make sure that you've logged in via `npm login` and that your package.json is correct. + +Once published, anyone will be able to install your components and then import them like so: + +```astro +--- +import { Bold, Capitalize } from '@example/my-components'; +--- +<Capitalize phrase={`Hello world`} /> +``` + +## Advanced + +We recommend a single `index.js` package entrypoint because this is what most users are familar with. However, in some rare scenarios you may want to have your users import each `.astro` component directly, in the same manner that you import `.astro` files in your own project. + +```astro +--- +import Capitalize from '@example/my-components/Capitalize.astro'; +--- +<Capitalize phrase={`Hello world`} /> +``` + +This is a less common scenario, and we only recommend it if you have good reason. Because Astro is completely rendered at build-time, there are no client-side performance concerns to our default recommendation to export your components from a single `index.js` file. + +To support importing by file within your package, add each file to your **package.json** `exports` map: + +```diff +{ + "name": "@example/my-components", + "version": "1.0.0", + "exports": { +- ".": "./index.js", ++ "./Bold.astro": "./Bold.astro", ++ "./Capitalize.astro": "./Capitalize.astro" + } +} +```
\ No newline at end of file diff --git a/docs/src/pages/guides/styling.md b/docs/src/pages/guides/styling.md new file mode 100644 index 000000000..74308eb16 --- /dev/null +++ b/docs/src/pages/guides/styling.md @@ -0,0 +1,511 @@ +--- +layout: ~/layouts/Main.astro +title: Styling & CSS +--- + +Astro includes special handling to make writing CSS as easy as possible. Styling inside of Astro components is done by adding a `<style>` tag anywhere. + +By default, all Astro component styles are **scoped**, meaning they only apply to the current component. These styles are automatically extracted and optimized for you in the final build, so that you don't need to worry about style loading. + +To create global styles, add a `:global()` wrapper around a selector (the same as if you were using [CSS Modules][css-modules]). + +```html +<!-- src/components/MyComponent.astro --> +<style> + /* Scoped class selector within the component */ + .scoped { + font-weight: bold; + } + /* Scoped element selector within the component */ + h1 { + color: red; + } + /* Global style */ + :global(h1) { + font-size: 32px; + } +</style> + +<div class="scoped">I’m a scoped style and only apply to this component</div> +<h1>I have both scoped and global styles</h1> +``` + +📚 Read our full guide on [Astro component syntax](/core-concepts/astro-components#css-styles) to learn more about using the `<style>` tag. + +## Cross-Browser Compatibility + +We also automatically add browser prefixes using [Autoprefixer][autoprefixer]. By default, Astro loads the [Browserslist defaults][browserslist-defaults], but you may also specify your own by placing a [Browserslist][browserslist] file in your project root. + +--- + +## Supported Styling Options + +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` | ✅ | ✅ | N/A¹ | +| `.jsx` \| `.tsx` | ✅ | ❌ | ✅ | +| `.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)_ + +All styles in Astro are automatically [**autoprefixed**](#cross-browser-compatibility) and optimized, so you can just write CSS and we’ll handle the rest ✨. + +--- + +## Frameworks and Libraries + +### 📘 React / Preact + +`.jsx` files support both global CSS and CSS Modules. To enable the latter, use the `.module.css` extension (or `.module.scss`/`.module.sass` if using Sass). + +```js +import './global.css'; // include global CSS +import Styles from './styles.module.css'; // Use CSS Modules (must end in `.module.css`, `.module.scss`, or `.module.sass`!) +``` + +### 📗 Vue + +Vue in Astro supports the same methods as `vue-loader` does: + +- [Scoped CSS][vue-scoped] +- [CSS Modules][vue-css-modules] + +### 📕 Svelte + +Svelte in Astro also works exactly as expected: [Svelte Styling Docs][svelte-style]. + +### 👓 Sass + +Astro also supports [Sass][sass] out-of-the-box. To enable for each framework: + +- **Astro**: `<style lang="scss">` or `<style lang="sass">` +- **React** / **Preact**: `import Styles from './styles.module.scss'`; +- **Vue**: `<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. + +### 🍃 Tailwind + +Astro can be configured to use [Tailwind][tailwind] easily! Install the dependencies: + +``` +npm install --save-dev tailwindcss +``` + +And also create a `tailwind.config.js` in your project root: + +```js +// tailwind.config.js +module.exports = { + mode: 'jit', + purge: ['./public/**/*.html', './src/**/*.{astro,js,jsx,ts,tsx,vue}'], + // more options here +}; +``` + +Be sure to add the config path to `astro.config.mjs`, so that Astro enables JIT support in the dev server. + +```diff + // astro.config.mjs + export default { ++ devOptions: { ++ tailwindConfig: './tailwind.config.js', ++ }, + }; +``` + +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 +/* public/global.css */ +@tailwind base; +@tailwind components; +@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. + +### Importing from npm + +If you want to import third-party libraries into an Astro component, you can use a `<style lang="scss">` tag to enable [Sass][sass] and use the [@use][sass-use] rule. + +```html +<!-- Loads Boostrap --> +<style lang="scss"> + @use "bootstrap/scss/bootstrap"; +</style> +``` + +## Bundling + +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 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!) + +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 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 + +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). + +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…** + +- Developers wanting to try out something new in regard to styling +- Developers that would appreciate some strong opinions in CSS architecture + +**This approach is **NOT** good for…** + +- 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: + +### Hybrid Scoped + Utility CSS + +#### 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 +--- +// src/components/Button.astro --> +--- +<style lang="scss"> + /* ✅ Locally scoped! */ + .btn { + padding: 0.5em 1em; + border-radius: 3px; + font-weight: 700; + } +</style> +<button type="button" class="btn"> + <slot></slot> +</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._ + +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()` escape hatch, 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: + +```jsx +--- +// src/components/Nav.astro +import Button from './Button.astro'; +--- + +<style lang="scss"> + .nav :global(.btn) { + /* ❌ This will fight with <Button>’s styles */ + } +</style> + +<nav class="nav"> + <Button>Menu</Button> +</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`). + +Instead, let `<Button>` control its own styles, and try a prop: + +```jsx +--- +// src/components/Button.astro +const { theme } = Astro.props; +--- +<style lang="scss"> + .btn { + /* ✅ <Button> is now back in control of its own styling again! */ + [data-theme='nav'] { + // nav-friendly styles here… + } + } +</style> + +<button type="button" data-theme={theme}> + <slot></slot> +</button> +``` + +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. + +#### 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. + +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: + +- [margin](https://github.com/drwpow/sass-utils#-margin--padding) +- [padding](https://github.com/drwpow/sass-utils#-margin--padding) +- [text/background color](https://github.com/drwpow/sass-utils#-color) +- [font size and family](https://github.com/drwpow/sass-utils#%F0%9F%85%B0%EF%B8%8F-font--text) +- [default element styling](https://github.com/kognise/water.css) + +In Astro, we recommend the following setup for this: + +```html +<head> + <link rel="stylesheet" href="/styles/global.css" /> +</head> +``` + +And in your local filesystem, you can even use Sass’ [@use][sass-use] to combine files together effortlessly: + +``` +├── public/ +│ └── styles/ +│ ├── _base.scss +│ ├── _tokens.scss +│ ├── _typography.scss +│ ├── _utils.scss +│ └── global.scss +└── src/ + └── (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. + +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. + +### 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** philosphy: + +1. Split your app into Layout Components and Base Components +1. Avoid Flexbox and Grid libraries (write your own!) +1. Avoid `margin` on a component wrapper +1. Avoid global media queries + +#### 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): + +``` +|---------------| +| 1 | +|-------+-------| +| 2 | 2 | +|---+---|---+---| +| 3 | 3 | 3 | 3 | +|---+---+---+---| +| 3 | 3 | 3 | 3 | +|---+---+---+---| +``` + +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 +--- +// src/pages/index.astro + +import Nav from '../components/Nav.astro'; +import BigPost from '../components/BigPost.astro'; +import Grid from '../components/Grid.astro'; +import MediumPosts from '../components/MediumPosts.astro'; +import SmallPosts from '../components/SmallPosts.astro'; +import Footer from '../components/Footer.astro'; +--- +<html> + <body> + <Nav /> + + <Grid> + <BigPost /> + <MediumPosts /> + <SmallPosts /> + </Grid> + + <Footer /> + </body> +</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 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: + +```jsx +--- +// src/pages/index.astro + +import Nav from '../components/Nav.astro'; +import BigPost from '../components/BigPost.astro'; +import MediumPost from '../components/MediumPost.astro'; +import SmallPost from '../components/SmallPost.astro'; +import Footer from '../components/Footer.astro'; +--- + +<html> + <head> + <style lang="scss"> + .wrapper { + max-width: 60rem; + margin-right: auto; + margin-left: auto; + padding-right: 2rem; + padding-left: 2rem; + } + + .grid { + display: grid; + grid-gap: 1.5rem; + grid-template columns: 1fr 1fr 1fr 1fr; + } + + .big-post { + grid-column: span 4; + } + + .medium-post { + grid-column: span 2; + } + + .small-post { + grid-column: span 1; + } + </style> + </head> + <body> + <Nav /> + + <div class="wrapper"> + <div class="grid"> + <div class="big-post"><BigPost postId={12345} /></div> + + <div class="medium-post"><MediumPost postId={12345} /></div> + <div class="medium-post"><MediumPost postId={12345} /></div> + + <div class="small-post"><SmallPost postId={12345} /></div> + <div class="small-post"><SmallPost postId={12345} /></div> + <div class="small-post"><SmallPost postId={12345} /></div> + <div class="small-post"><SmallPost postId={12345} /></div> + <div class="small-post"><SmallPost postId={12345} /></div> + <div class="small-post"><SmallPost postId={12345} /></div> + <div class="small-post"><SmallPost postId={12345} /></div> + <div class="small-post"><SmallPost postId={12345} /></div> + </div> + </div> + + <Footer /> + </body> +</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. + +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.** + +💁 **Why this works well in Astro**: In Astro, anything can be a `.astro` component, and you never incur performance problems no matter how many components you add. But the main benefit to [Layout isolation][layout-isolated] is how much it cuts down on the amount of CSS you need. + +#### 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). + +Many front-end developers experience the following train of thought: + +1. I should reuse as much CSS as possible (_good!_) +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—_) + +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: + +- [Flexbox Froggy](https://flexboxfroggy.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. + +💁 **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 + +In other words, don’t do this: + +```jsx +<!-- src/components/MyComponent.astro --> +<style lang="scss"> + .wrapper { + /* ❌ Don’t do this! */ + margin-top: 3rem; + } +</style> + +<div class="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]. + +When you have components that rearrage, 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. + +#### 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 awkardly 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). + +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. + +💁 **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 + +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 +- [**In defense of utility-first CSS**][utility-css] by Sarah Dayan + +Also please check out the [Stylelint][stylelint] project to whip your styles into shape. You lint your JS, why not your CSS? + +[autoprefixer]: https://github.com/postcss/autoprefixer +[bem]: http://getbem.com/introduction/ +[box-model]: https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model +[browserslist]: https://github.com/browserslist/browserslist +[browserslist-defaults]: https://github.com/browserslist/browserslist#queries +[cassie-evans-css]: https://twitter.com/cassiecodes/status/1392756828786790400?s=20 +[container-queries]: https://ishadeed.com/article/say-hello-to-css-container-queries/ +[css-modules]: https://github.com/css-modules/css-modules +[css-treeshaking]: https://css-tricks.com/how-do-you-remove-unused-css-from-a-site/ +[fouc]: https://en.wikipedia.org/wiki/Flash_of_unstyled_content +[layout-isolated]: https://web.archive.org/web/20210227162315/https://visly.app/blogposts/layout-isolated-components +[issues]: https://github.com/snowpackjs/astro/issues +[magic-number]: https://css-tricks.com/magic-numbers-in-css/ +[material-ui]: https://material.io/components +[peace-on-css]: https://didoo.medium.com/let-there-be-peace-on-css-8b26829f1be0 +[sass]: https://sass-lang.com/ +[sass-use]: https://sass-lang.com/documentation/at-rules/use +[smacss]: http://smacss.com/ +[styled-components]: https://styled-components.com/ +[styled-jsx]: https://github.com/vercel/styled-jsx +[stylelint]: https://stylelint.io/ +[svelte-style]: https://svelte.dev/docs#style +[tailwind]: https://tailwindcss.com +[tailwind-utilities]: https://tailwindcss.com/docs/adding-new-utilities#using-css +[utility-css]: https://frontstuff.io/in-defense-of-utility-first-css +[vue-css-modules]: https://vue-loader.vuejs.org/guide/css-modules.html +[vue-scoped]: https://vue-loader.vuejs.org/guide/scoped-css.html |