summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/@types/postcss-icss-keyframes.d.ts5
-rw-r--r--src/compiler/optimize/postcss-scoped-styles/index.ts20
-rw-r--r--src/compiler/optimize/styles.ts15
3 files changed, 31 insertions, 9 deletions
diff --git a/src/@types/postcss-icss-keyframes.d.ts b/src/@types/postcss-icss-keyframes.d.ts
new file mode 100644
index 000000000..14c330b6e
--- /dev/null
+++ b/src/@types/postcss-icss-keyframes.d.ts
@@ -0,0 +1,5 @@
+declare module 'postcss-icss-keyframes' {
+ import type { Plugin } from 'postcss';
+
+ export default function (options: { generateScopedName(keyframesName: string, filepath: string, css: string): string }): Plugin;
+}
diff --git a/src/compiler/optimize/postcss-scoped-styles/index.ts b/src/compiler/optimize/postcss-scoped-styles/index.ts
index 01c0acd94..23350869c 100644
--- a/src/compiler/optimize/postcss-scoped-styles/index.ts
+++ b/src/compiler/optimize/postcss-scoped-styles/index.ts
@@ -1,4 +1,4 @@
-import { Plugin } from 'postcss';
+import { Declaration, Plugin } from 'postcss';
interface AstroScopedOptions {
className: string;
@@ -11,17 +11,24 @@ interface Selector {
}
const CSS_SEPARATORS = new Set([' ', ',', '+', '>', '~']);
+const KEYFRAME_PERCENT = /\d+\.?\d*%/;
/** HTML tags that should never get scoped classes */
export const NEVER_SCOPED_TAGS = new Set<string>(['base', 'body', 'font', 'frame', 'frameset', 'head', 'html', 'link', 'meta', 'noframes', 'noscript', 'script', 'style', 'title']);
/**
- * Scope Selectors
+ * Scope Rules
* Given a selector string (`.btn>span,.nav>span`), add an additional CSS class to every selector (`.btn.myClass>span.myClass,.nav.myClass>span.myClass`)
* @param {string} selector The minified selector string to parse. Cannot contain arbitrary whitespace (other than child selector syntax).
* @param {string} className The CSS class to apply.
*/
-export function scopeSelectors(selector: string, className: string) {
+export function scopeRule(selector: string, className: string) {
+ // if this is a keyframe keyword, return original selector
+ if (selector === 'from' || selector === 'to' || KEYFRAME_PERCENT.test(selector)) {
+ return selector;
+ }
+
+ // For everything else, parse & scope
const c = className.replace(/^\.?/, '.'); // make sure class always has leading '.'
const selectors: Selector[] = [];
let ss = selector; // final output
@@ -30,9 +37,12 @@ export function scopeSelectors(selector: string, className: string) {
{
let start = 0;
let lastValue = '';
+ let parensOpen = false;
for (let n = 0; n < ss.length; n++) {
const isEnd = n === selector.length - 1;
- if (isEnd || CSS_SEPARATORS.has(selector[n])) {
+ if (selector[n] === '(') parensOpen = true;
+ if (selector[n] === ')') parensOpen = false;
+ if (isEnd || (parensOpen === false && CSS_SEPARATORS.has(selector[n]))) {
lastValue = selector.substring(start, isEnd ? undefined : n);
if (!lastValue) continue;
selectors.push({ start, end: isEnd ? n + 1 : n, value: lastValue });
@@ -90,7 +100,7 @@ export default function astroScopedStyles(options: AstroScopedOptions): Plugin {
return {
postcssPlugin: '@astro/postcss-scoped-styles',
Rule(rule) {
- rule.selector = scopeSelectors(rule.selector, options.className);
+ rule.selector = scopeRule(rule.selector, options.className);
},
};
}
diff --git a/src/compiler/optimize/styles.ts b/src/compiler/optimize/styles.ts
index 65b429fef..807d869c9 100644
--- a/src/compiler/optimize/styles.ts
+++ b/src/compiler/optimize/styles.ts
@@ -2,8 +2,8 @@ import crypto from 'crypto';
import fs from 'fs';
import path from 'path';
import autoprefixer from 'autoprefixer';
-import esbuild from 'esbuild';
import postcss, { Plugin } from 'postcss';
+import postcssKeyframes from 'postcss-icss-keyframes';
import findUp from 'find-up';
import sass from 'sass';
import type { RuntimeMode } from '../../@types/astro';
@@ -27,11 +27,9 @@ const getStyleType: Map<string, StyleType> = new Map([
['.sass', 'sass'],
['.scss', 'scss'],
['css', 'css'],
- ['postcss', 'postcss'],
['sass', 'sass'],
['scss', 'scss'],
['text/css', 'css'],
- ['text/postcss', 'postcss'],
['text/sass', 'sass'],
['text/scss', 'scss'],
]);
@@ -134,7 +132,16 @@ async function transformStyle(code: string, { type, filename, scopedClass, mode
// 2b. Astro scoped styles (always on)
postcssPlugins.push(astroScopedStyles({ className: scopedClass }));
- // 2c. Autoprefixer (always on)
+ // 2c. Scoped @keyframes
+ postcssPlugins.push(
+ postcssKeyframes({
+ generateScopedName(keyframesName) {
+ return `${keyframesName}-${scopedClass}`;
+ },
+ })
+ );
+
+ // 2d. Autoprefixer (always on)
postcssPlugins.push(autoprefixer());
// 2e. Run PostCSS