Skip to content

Commit e60cf64

Browse files
author
Artem Nistuley
committed
feat: add layered style export
1 parent 915dbbe commit e60cf64

8 files changed

Lines changed: 91 additions & 2 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ const superdoc = new SuperDoc({
7272
});
7373
```
7474

75+
Optional layered CSS mode:
76+
77+
```css
78+
@layer reset, superdoc, app;
79+
@import 'superdoc/style.layered.css';
80+
@import 'your-app.css' layer(app);
81+
```
82+
7583
Or use the CDN:
7684

7785
```html

apps/docs/editor/theming/overview.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ document.documentElement.classList.add(theme);
1919

2020
Five properties theme the entire UI.
2121

22+
## Optional layered stylesheet
23+
24+
Default usage remains:
25+
26+
```javascript
27+
import 'superdoc/style.css';
28+
```
29+
30+
If your application uses CSS cascade layers and you want explicit layer ordering, use:
31+
32+
```css
33+
@layer reset, superdoc, app;
34+
@import 'superdoc/style.layered.css';
35+
@import 'your-app.css' layer(app);
36+
```
37+
2238
## `createTheme()`
2339

2440
Pass a config object, get back a CSS class name. Apply it to `<html>`.

apps/docs/getting-started/theming.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@ new SuperDoc({ selector: '#editor', document: 'contract.docx' });
3333

3434
Toolbar buttons, comments sidebar, dropdowns, context menu, search bar, dialog surfaces. Any chrome SuperDoc renders. The document content itself (paragraphs, headings, tables) renders with its own styles from the DOCX.
3535

36+
## Optional layered stylesheet
37+
38+
By default, you can keep using:
39+
40+
```javascript
41+
import 'superdoc/style.css';
42+
```
43+
44+
If your app uses cascade layers and you want SuperDoc styles in a named layer, use the optional layered entrypoint:
45+
46+
```css
47+
@layer reset, superdoc, app;
48+
@import 'superdoc/style.layered.css';
49+
@import 'your-app.css' layer(app);
50+
```
51+
3652
## When to drop to CSS variables
3753

3854
`createTheme()` covers the common case. For component-level overrides, use the `vars` option or set raw `--sd-*` variables in your stylesheet:

packages/superdoc/AGENTS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,19 @@ const theme = createTheme({
134134
document.documentElement.classList.add(theme);
135135
```
136136

137+
CSS entrypoints:
138+
139+
- `superdoc/style.css` — standard stylesheet.
140+
- `superdoc/style.layered.css` — optional layered stylesheet wrapped in `@layer superdoc`.
141+
142+
Recommended layered setup:
143+
144+
```css
145+
@layer reset, superdoc, app;
146+
@import 'superdoc/style.layered.css';
147+
@import 'your-app.css' layer(app);
148+
```
149+
137150
Docs: https://docs.superdoc.dev/getting-started/theming
138151

139152
## Document Engine — programmatic access

packages/superdoc/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@
8282
"types": "./dist/superdoc/src/public/legacy/file-zipper.d.ts",
8383
"import": "./dist/super-editor/file-zipper.es.js"
8484
},
85-
"./style.css": "./dist/style.css"
85+
"./style.css": "./dist/style.css",
86+
"./style.layered.css": "./dist/style.layered.css"
8687
},
8788
"types": "./dist/superdoc/src/public/index.d.ts",
8889
"typesVersions": {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
4+
export default function layeredCssPlugin() {
5+
return {
6+
name: 'superdoc-layered-css',
7+
writeBundle(outputOptions, bundle) {
8+
const cssAssets = Object.entries(bundle).filter(([, chunk]) => {
9+
return chunk.type === 'asset' && typeof chunk.fileName === 'string' && chunk.fileName.endsWith('.css');
10+
});
11+
12+
if (cssAssets.length === 0) {
13+
return;
14+
}
15+
16+
const targetAsset =
17+
cssAssets.find(([fileName]) => fileName === 'style.css') ??
18+
cssAssets[0];
19+
20+
const [, chunk] = targetAsset;
21+
const source = typeof chunk.source === 'string'
22+
? chunk.source
23+
: Buffer.from(chunk.source).toString('utf8');
24+
25+
const layeredCss = `@layer superdoc{${source}}\n`;
26+
const outDir = outputOptions.dir ?? path.dirname(outputOptions.file ?? '');
27+
const layeredFilePath = path.join(outDir, 'style.layered.css');
28+
29+
fs.writeFileSync(layeredFilePath, layeredCss);
30+
},
31+
};
32+
}

packages/superdoc/vite.config.cdn.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import vue from '@vitejs/plugin-vue';
22
import { defineConfig } from 'vite';
33
import { version } from './package.json';
44
import { getAliases } from './vite.config.js';
5+
import layeredCssPlugin from './vite-plugin-layered-css.mjs';
56

67
// Standalone browser bundle for CDN / <script> tag consumption.
78
// Exposes `window.SuperDoc`. Inlines all runtime deps (Vue, ProseMirror,
89
// Yjs, Hocuspocus) so a single <script> tag is enough — these peers are
910
// ESM-only and can't be loaded as globals. Only pdfjs-dist stays external
1011
// because of its size; PDF viewing requires the ESM + import-map path.
1112
export default defineConfig(({ command }) => {
12-
const plugins = [vue()];
13+
const plugins = [vue(), layeredCssPlugin()];
1314
const isDev = command === 'serve';
1415

1516
return {

packages/superdoc/vite.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { fileURLToPath, URL } from 'node:url';
99
import { nodePolyfills } from 'vite-plugin-node-polyfills';
1010
import { visualizer } from 'rollup-plugin-visualizer';
1111
import vue from '@vitejs/plugin-vue'
12+
import layeredCssPlugin from './vite-plugin-layered-css.mjs';
1213

1314
import { version } from './package.json';
1415
import sourceResolve from '../../vite.sourceResolve';
@@ -118,6 +119,7 @@ export default defineConfig(({ mode, command }) => {
118119
const skipDts = process.env.SUPERDOC_SKIP_DTS === '1';
119120
const plugins = [
120121
vue(),
122+
layeredCssPlugin(),
121123
!skipDts && dts({
122124
// Foundational sources (superdoc, super-editor, document-api) are
123125
// always included; relocation patterns come from the canonical

0 commit comments

Comments
 (0)