Update Snowpack, Add CSS Modules SSR (#116)
* Add CSS Modules SSR * Update docs
This commit is contained in:
parent
fe63d341cb
commit
3263c02d77
11 changed files with 103 additions and 32 deletions
84
README.md
84
README.md
|
@ -82,38 +82,81 @@ _Are we missing your favorite state management library? Add it to the list above
|
|||
|
||||
### 💅 Styling
|
||||
|
||||
If you’ve used [Svelte][svelte]’s styles before, Astro works almost the same way. In any `.astro` file, start writing styles in a `<style>` tag like so:
|
||||
Styling in Astro is meant to be as flexible as you’d like it to be! The following options are all supported:
|
||||
|
||||
| Framework | Global CSS | Scoped CSS | CSS Modules |
|
||||
| :--------------- | :--------: | :--------: | :---------: |
|
||||
| Astro (`.astro`) | ✅ | ✅ | N/A¹ |
|
||||
| React / Preact | ✅ | ❌ | ✅ |
|
||||
| Vue | ✅ | ✅ | ✅ |
|
||||
| Svelte | ✅ | ✅ | ❌ |
|
||||
|
||||
¹ _`.astro` files have no runtime, therefore Scoped CSS takes the place of CSS Modules (styles are still scoped to components, but don’t need dynamic values)_
|
||||
|
||||
#### 🖍 Styling by Framework
|
||||
|
||||
##### Astro
|
||||
|
||||
Styling in an Astro component is done by adding a `<style>` tag anywhere. By default, all styles are **scoped**, meaning they only apply to the current component. To create global styles, add a `:global()` wrapper around a selector (the same as if you were using [CSS Modules][css-modules]).
|
||||
|
||||
```html
|
||||
<!-- astro/components/MyComponent.astro -->
|
||||
|
||||
<style>
|
||||
/* Scoped class selector within the component */
|
||||
.scoped {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="scoped">I’m a scoped style</div>
|
||||
```
|
||||
/* Scoped element selector within the component */
|
||||
h1 {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#### 👓 Sass
|
||||
|
||||
Astro also supports [Sass][sass] out-of-the-box; no configuration needed:
|
||||
|
||||
```html
|
||||
<style lang="scss">
|
||||
@use "../tokens" as *;
|
||||
|
||||
.title {
|
||||
color: $color.gray;
|
||||
/* Global style */
|
||||
:global(h1) {
|
||||
font-size: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1 class="title">Title</h1>
|
||||
<div class="scoped">I’m a scoped style and only apply to this component</div>
|
||||
<h1>I have both scoped and global styles</h1>
|
||||
```
|
||||
|
||||
Supports:
|
||||
**Tips**
|
||||
|
||||
- `lang="scss"`: load as the `.scss` extension
|
||||
- `lang="sass"`: load as the `.sass` extension (no brackets; indent-style)
|
||||
- `<style>` tags within `.astro` files will be extracted and optimized for you on build. So you can write CSS without worrying too much about delivery.
|
||||
- For best result, only have one `<style>` tag per-Astro component. This isn’t necessarily a limitation, but it may result in better optimization at buildtime.
|
||||
- If you want to import third-party libraries into an Astro component, you can use [Sass][sass]! In particular, [@use][sass-use] may come in handy (e.g. `@use "bootstrap/scss/bootstrap"`);
|
||||
|
||||
#### 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">`
|
||||
|
||||
#### 🦊 Autoprefixer
|
||||
|
||||
|
@ -394,11 +437,16 @@ const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of po
|
|||
[autoprefixer]: https://github.com/postcss/autoprefixer
|
||||
[browserslist]: https://github.com/browserslist/browserslist
|
||||
[collections]: #-collections-beta
|
||||
[css-modules]: https://github.com/css-modules/css-modules
|
||||
[config]: #%EF%B8%8F-configuration
|
||||
[fetch-content]: #fetchContent--
|
||||
[intersection-observer]: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
|
||||
[request-idle-cb]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
||||
[sass]: https://sass-lang.com/
|
||||
[sass-use]: https://sass-lang.com/documentation/at-rules/use
|
||||
[svelte]: https://svelte.dev
|
||||
[svelte-style]: https://svelte.dev/docs#style
|
||||
[tailwind]: https://tailwindcss.com
|
||||
[tailwind-utilities]: https://tailwindcss.com/docs/adding-new-utilities#using-css
|
||||
[vue-css-modules]: https://vue-loader.vuejs.org/guide/css-modules.html
|
||||
[vue-scoped]: https://vue-loader.vuejs.org/guide/scoped-css.html
|
||||
|
|
12
package-lock.json
generated
12
package-lock.json
generated
|
@ -3957,9 +3957,9 @@
|
|||
"integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw=="
|
||||
},
|
||||
"snowpack": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/snowpack/-/snowpack-3.3.2.tgz",
|
||||
"integrity": "sha512-ZMTnEFuxRgOzIsbMKHVv5ssP1Bi1fTsLrjpDBkyXj0LH9+q6igynhAJqrvoOhyoH8IW4k3yN/WqE8qzXbMFTEQ==",
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/snowpack/-/snowpack-3.3.4.tgz",
|
||||
"integrity": "sha512-Yj4WlVwLHH9LRb0MwTklIWPtE4L20RMFDpuWr75kS6wvHtIJWUf0/rma8PcoLMKYtSJirsDmrglI9rceFIx+Zg==",
|
||||
"requires": {
|
||||
"cli-spinners": "^2.5.0",
|
||||
"default-browser-id": "^2.0.0",
|
||||
|
@ -3997,9 +3997,9 @@
|
|||
}
|
||||
},
|
||||
"socks": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.0.tgz",
|
||||
"integrity": "sha512-mNmr9owlinMplev0Wd7UHFlqI4ofnBnNzFuzrm63PPaHgbkqCFe4T5LzwKmtQ/f2tX0NTpcdVLyD/FHxFBstYw==",
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
|
||||
"integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
|
||||
"requires": {
|
||||
"ip": "^1.1.5",
|
||||
"smart-buffer": "^4.1.0"
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
"rollup": "^2.43.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sass": "^1.32.8",
|
||||
"snowpack": "^3.3.2",
|
||||
"snowpack": "^3.3.4",
|
||||
"svelte": "^3.35.0",
|
||||
"tiny-glob": "^0.2.8",
|
||||
"unified": "^9.2.1",
|
||||
|
|
|
@ -20,6 +20,7 @@ setup(StylesSSR, './fixtures/astro-styles-ssr');
|
|||
StylesSSR('Has <link> tags', async ({ runtime }) => {
|
||||
const MUST_HAVE_LINK_TAGS = [
|
||||
'/_astro/components/ReactCSS.css',
|
||||
'/_astro/components/ReactModules.module.css',
|
||||
'/_astro/components/SvelteScoped.svelte.css',
|
||||
'/_astro/components/VueCSS.vue.css',
|
||||
'/_astro/components/VueModules.vue.css',
|
||||
|
@ -36,22 +37,28 @@ StylesSSR('Has <link> tags', async ({ runtime }) => {
|
|||
});
|
||||
|
||||
StylesSSR('Has correct CSS classes', async ({ runtime }) => {
|
||||
// TODO: remove this (temporary CI patch)
|
||||
if (process.version.startsWith('v14.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await runtime.load('/');
|
||||
const $ = doc(result.contents);
|
||||
|
||||
const MUST_HAVE_CLASSES = {
|
||||
'#react-css': 'react-title',
|
||||
'#react-modules': 'title', // ⚠️ this should be transformed
|
||||
'#vue-css': 'vue-title',
|
||||
'#vue-css-modules': 'title', // ⚠️ this is the inverse
|
||||
'#vue-modules': 'title', // ⚠️ this should also be transformed
|
||||
'#vue-scoped': 'vue-title', // also has data-v-* property
|
||||
'#svelte-scoped': 'svelte-title', // also has additional class
|
||||
};
|
||||
|
||||
for (const [selector, className] of Object.entries(MUST_HAVE_CLASSES)) {
|
||||
const el = $(selector);
|
||||
if (selector === '#vue-css-modules') {
|
||||
if (selector === '#react-modules' || selector === '#vue-modules') {
|
||||
// this will generate differently on Unix vs Windows. Here we simply test that it has transformed
|
||||
assert.not.equal(el.attr('class'), 'title');
|
||||
assert.match(el.attr('class'), new RegExp(`^_${className}_[A-Za-z0-9-_]+`)); // className should be transformed, surrounded by underscores and other stuff
|
||||
} else {
|
||||
// if this is not a CSS module, it should remain as expected
|
||||
assert.ok(el.attr('class').includes(className));
|
||||
|
|
|
@ -4,7 +4,7 @@ import './ReactCSS.css';
|
|||
function ReactCSS() {
|
||||
return (
|
||||
<h1 id="react-css" className="react-title">
|
||||
React CSS
|
||||
React Global CSS
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
|
11
test/fixtures/astro-styles-ssr/astro/components/ReactModules.jsx
vendored
Normal file
11
test/fixtures/astro-styles-ssr/astro/components/ReactModules.jsx
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
import Styles from './ReactModules.module.css';
|
||||
|
||||
function ReactModules() {
|
||||
return (
|
||||
<h1 id="react-modules" className={Styles.title}>
|
||||
React Modules
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
export default ReactModules;
|
3
test/fixtures/astro-styles-ssr/astro/components/ReactModules.module.css
vendored
Normal file
3
test/fixtures/astro-styles-ssr/astro/components/ReactModules.module.css
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
.title {
|
||||
font-family: fantasy;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<h1 id="svelte-scoped" class="svelte-title">Svelte Scoped</h1>
|
||||
<h1 id="svelte-scoped" class="svelte-title">Svelte Scoped CSS</h1>
|
||||
|
||||
<style>
|
||||
.svelte-title {
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
</style>
|
||||
|
||||
<template>
|
||||
<h1 id="vue-css" class="vue-title">Vue CSS Modules</h1>
|
||||
<h1 id="vue-css" class="vue-title">Vue Global CSS</h1>
|
||||
</template>
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
</style>
|
||||
|
||||
<template>
|
||||
<h1 id="vue-css-modules" :class="$style.title">Vue CSS Modules</h1>
|
||||
<h1 id="vue-modules" :class="$style.title">Vue CSS Modules</h1>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
import ReactCSS from '../components/ReactCSS.jsx';
|
||||
import ReactModules from '../components/ReactModules.jsx';
|
||||
import VueCSS from '../components/VueCSS.vue';
|
||||
import VueScoped from '../components/VueScoped.vue';
|
||||
import VueModules from '../components/VueModules.vue';
|
||||
|
@ -20,6 +21,7 @@ import SvelteScoped from '../components/SvelteScoped.svelte';
|
|||
<body>
|
||||
<div class="wrapper">
|
||||
<ReactCSS />
|
||||
<ReactModules />
|
||||
<VueCSS />
|
||||
<VueScoped />
|
||||
<VueModules />
|
||||
|
|
Loading…
Reference in a new issue