diff --git a/examples/rollup/src/App.svelte b/examples/rollup/src/App.svelte
index 745f8bcb..14345983 100644
--- a/examples/rollup/src/App.svelte
+++ b/examples/rollup/src/App.svelte
@@ -3,6 +3,7 @@
import typescript from "svelte-highlight/languages/typescript";
import atomOneDark from "svelte-highlight/styles/atom-one-dark";
import DynamicImport from "./DynamicImport.svelte";
+ import ScopedStyles from "./ScopedStyles.svelte";
const code = "const add = (a: number, b: number) => a + b;";
@@ -14,3 +15,5 @@
+
+
diff --git a/examples/rollup/src/ScopedStyles.svelte b/examples/rollup/src/ScopedStyles.svelte
new file mode 100644
index 00000000..191eb681
--- /dev/null
+++ b/examples/rollup/src/ScopedStyles.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@html github}
+ {@html atomOneDark}
+ {@html blackMetalDarkFuneral}
+
+
+
+
+
+
+
diff --git a/examples/routify/src/components/ScopedStyles.svelte b/examples/routify/src/components/ScopedStyles.svelte
new file mode 100644
index 00000000..191eb681
--- /dev/null
+++ b/examples/routify/src/components/ScopedStyles.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@html github}
+ {@html atomOneDark}
+ {@html blackMetalDarkFuneral}
+
+
+
+
+
+
+
diff --git a/examples/routify/src/routes/index.svelte b/examples/routify/src/routes/index.svelte
index 48dd90ed..a91d7470 100644
--- a/examples/routify/src/routes/index.svelte
+++ b/examples/routify/src/routes/index.svelte
@@ -3,6 +3,7 @@
import typescript from "svelte-highlight/languages/typescript";
import atomOneDark from "svelte-highlight/styles/atom-one-dark";
import DynamicImport from "../components/DynamicImport.svelte";
+ import ScopedStyles from "../components/ScopedStyles.svelte";
const code = "const add = (a: number, b: number) => a + b;";
@@ -14,3 +15,5 @@
+
+
diff --git a/examples/sveltekit/src/lib/ScopedStyles.svelte b/examples/sveltekit/src/lib/ScopedStyles.svelte
new file mode 100644
index 00000000..191eb681
--- /dev/null
+++ b/examples/sveltekit/src/lib/ScopedStyles.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@html github}
+ {@html atomOneDark}
+ {@html blackMetalDarkFuneral}
+
+
+
+
+
+
+
diff --git a/examples/sveltekit/src/routes/+page.svelte b/examples/sveltekit/src/routes/+page.svelte
index 70390d08..6e3e4740 100644
--- a/examples/sveltekit/src/routes/+page.svelte
+++ b/examples/sveltekit/src/routes/+page.svelte
@@ -3,6 +3,7 @@
import typescript from "svelte-highlight/languages/typescript";
import atomOneDark from "svelte-highlight/styles/atom-one-dark";
import DynamicImport from "$lib/DynamicImport.svelte";
+ import ScopedStyles from "$lib/ScopedStyles.svelte";
const code = "const add = (a: number, b: number) => a + b;";
@@ -14,3 +15,5 @@
+
+
diff --git a/examples/vite/src/App.svelte b/examples/vite/src/App.svelte
index 745f8bcb..14345983 100644
--- a/examples/vite/src/App.svelte
+++ b/examples/vite/src/App.svelte
@@ -3,6 +3,7 @@
import typescript from "svelte-highlight/languages/typescript";
import atomOneDark from "svelte-highlight/styles/atom-one-dark";
import DynamicImport from "./DynamicImport.svelte";
+ import ScopedStyles from "./ScopedStyles.svelte";
const code = "const add = (a: number, b: number) => a + b;";
@@ -14,3 +15,5 @@
+
+
diff --git a/examples/vite/src/ScopedStyles.svelte b/examples/vite/src/ScopedStyles.svelte
new file mode 100644
index 00000000..191eb681
--- /dev/null
+++ b/examples/vite/src/ScopedStyles.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@html github}
+ {@html atomOneDark}
+ {@html blackMetalDarkFuneral}
+
+
+
+
+
+
+
diff --git a/examples/vite@svelte-5/src/App.svelte b/examples/vite@svelte-5/src/App.svelte
index 745f8bcb..14345983 100644
--- a/examples/vite@svelte-5/src/App.svelte
+++ b/examples/vite@svelte-5/src/App.svelte
@@ -3,6 +3,7 @@
import typescript from "svelte-highlight/languages/typescript";
import atomOneDark from "svelte-highlight/styles/atom-one-dark";
import DynamicImport from "./DynamicImport.svelte";
+ import ScopedStyles from "./ScopedStyles.svelte";
const code = "const add = (a: number, b: number) => a + b;";
@@ -14,3 +15,5 @@
+
+
diff --git a/examples/vite@svelte-5/src/ScopedStyles.svelte b/examples/vite@svelte-5/src/ScopedStyles.svelte
new file mode 100644
index 00000000..191eb681
--- /dev/null
+++ b/examples/vite@svelte-5/src/ScopedStyles.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@html github}
+ {@html atomOneDark}
+ {@html blackMetalDarkFuneral}
+
+
+
+
+
+
+
diff --git a/examples/webpack/src/App.svelte b/examples/webpack/src/App.svelte
index 745f8bcb..14345983 100644
--- a/examples/webpack/src/App.svelte
+++ b/examples/webpack/src/App.svelte
@@ -3,6 +3,7 @@
import typescript from "svelte-highlight/languages/typescript";
import atomOneDark from "svelte-highlight/styles/atom-one-dark";
import DynamicImport from "./DynamicImport.svelte";
+ import ScopedStyles from "./ScopedStyles.svelte";
const code = "const add = (a: number, b: number) => a + b;";
@@ -14,3 +15,5 @@
+
+
diff --git a/examples/webpack/src/ScopedStyles.svelte b/examples/webpack/src/ScopedStyles.svelte
new file mode 100644
index 00000000..191eb681
--- /dev/null
+++ b/examples/webpack/src/ScopedStyles.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@html github}
+ {@html atomOneDark}
+ {@html blackMetalDarkFuneral}
+
+
+
+
+
+
+
diff --git a/scripts/build-styles.ts b/scripts/build-styles.ts
index a052f0f9..ff0c6690 100644
--- a/scripts/build-styles.ts
+++ b/scripts/build-styles.ts
@@ -1,33 +1,39 @@
import { $, Glob } from "bun";
import path from "node:path";
+import postcss from "postcss";
import { createMarkdown } from "./utils/create-markdown";
-import { minifyCss } from "./utils/minify-css";
+import { minifyCss, minifyPreset } from "./utils/minify-css";
+import { pluginScopedStyles } from "./utils/plugin-scoped-styles";
import { toCamelCase } from "./utils/to-pascal-case";
import { writeTo } from "./utils/write-to";
-import postcss from "postcss";
+
+const preserveLicenseComments = {
+ // Preserve license comments.
+ remove: (comment: string) => !/(License|Author)/i.test(comment),
+} as const;
const createScopedStyles = (props: { source: string; moduleName: string }) => {
const { source, moduleName } = props;
return postcss([
- {
- postcssPlugin: "postcss-plugin:scoped-styles",
- Once(root) {
- root.walkRules((rule) => {
- rule.selectors = rule.selectors.map((selector) => {
- if (/^pre /.test(selector)) {
- selector = `pre.${moduleName}${selector.replace(/^pre /, " ")}`;
- } else {
- selector = `.${moduleName} ${selector}`;
- }
- return selector;
- });
- });
- },
- },
+ pluginScopedStyles({ moduleName }),
+ ...minifyPreset(preserveLicenseComments),
]).process(source).css;
};
+const createJsStyles = (props: { moduleName: string; content: string }) => {
+ const { moduleName, content } = props;
+
+ const css = minifyCss(
+ // Escape backticks for JS template literal.
+ content.replace(/\`/g, "\\`"),
+ preserveLicenseComments,
+ );
+
+ return `const ${moduleName} = \`\`;\n
+ export default ${moduleName};\n`;
+};
+
export type ModuleNames = Array<{ name: string; moduleName: string }>;
export async function buildStyles() {
@@ -61,22 +67,10 @@ export async function buildStyles() {
const content = await Bun.file(absPath).text();
const css_minified = minifyCss(content);
- // Escape backticks for JS template literal.
- const content_css_for_js = minifyCss(content.replace(/\`/g, "\\`"), {
- remove: (comment) => {
- if (/(License|Author)/i.test(comment)) {
- // Preserve license comments.
- return false;
- }
-
- return true;
- },
- });
-
- const exportee = `const ${moduleName} = \`\`;\n
- export default ${moduleName};\n`;
-
- await writeTo(`src/styles/${name}.js`, exportee);
+ await writeTo(
+ `src/styles/${name}.js`,
+ createJsStyles({ moduleName, content }),
+ );
await writeTo(
`src/styles/${name}.d.ts`,
`export { ${moduleName} as default } from "./";\n`,
@@ -85,6 +79,17 @@ export async function buildStyles() {
const scoped_style = createScopedStyles({ source: content, moduleName });
+ await writeTo(
+ `src/styles/${name}.scoped.js`,
+ createJsStyles({ moduleName, content: scoped_style }),
+ );
+ await writeTo(
+ `src/styles/${name}.scoped.d.ts`,
+ `export { ${moduleName} as default } from "./";\n`,
+ );
+
+ await writeTo(`src/styles/${name}.scoped.css`, scoped_style);
+
scoped_styles += scoped_style;
} else {
// Copy over other file types, like images.
@@ -144,6 +149,8 @@ export async function buildStyles() {
// Don't format metadata used in docs.
await Bun.write("www/data/styles.json", JSON.stringify(styles));
+
+ // For performance, a dedictated CSS file is used for scoped styles in docs.
await Bun.write(
"www/data/scoped-styles.css",
minifyCss(scoped_styles, { removeAll: true }),
diff --git a/scripts/utils/minify-css.ts b/scripts/utils/minify-css.ts
index 4b771c06..57456e69 100644
--- a/scripts/utils/minify-css.ts
+++ b/scripts/utils/minify-css.ts
@@ -2,11 +2,13 @@ import cssnano from "cssnano";
import litePreset, { type LiteOptions } from "cssnano-preset-lite";
import postcss from "postcss";
+export const minifyPreset = (
+ discardComments?: LiteOptions["discardComments"],
+) => [cssnano({ preset: litePreset({ discardComments }) })];
+
export const minifyCss = (
css: string,
discardComments?: LiteOptions["discardComments"],
) => {
- return postcss([
- cssnano({ preset: litePreset({ discardComments }) }),
- ]).process(css).css;
+ return postcss(minifyPreset(discardComments)).process(css).css;
};
diff --git a/scripts/utils/plugin-scoped-styles.ts b/scripts/utils/plugin-scoped-styles.ts
new file mode 100644
index 00000000..2deb59c5
--- /dev/null
+++ b/scripts/utils/plugin-scoped-styles.ts
@@ -0,0 +1,26 @@
+import type { Plugin } from "postcss";
+
+const RE_PRE = /^pre /;
+
+export function pluginScopedStyles({
+ moduleName,
+}: {
+ moduleName: string;
+}): Plugin {
+ return {
+ postcssPlugin: "postcss-plugin:scoped-styles",
+ Once(root) {
+ root.walkRules((rule) => {
+ rule.selectors = rule.selectors.map((selector) => {
+ if (RE_PRE.test(selector)) {
+ selector = `pre.${moduleName}${selector.replace(RE_PRE, " ")}`;
+ } else {
+ selector = `.${moduleName} ${selector}`;
+ }
+
+ return selector;
+ });
+ });
+ },
+ };
+};