mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-10 06:42:31 +02:00
feat(theme): new CSS cascade layers plugin + built-in v4.useCssCascadeLayers
future flag (#11142)
Co-authored-by: slorber <749374+slorber@users.noreply.github.com>
This commit is contained in:
parent
a301b24d64
commit
abd04a2b71
26 changed files with 894 additions and 0 deletions
3
packages/docusaurus-plugin-css-cascade-layers/.npmignore
Normal file
3
packages/docusaurus-plugin-css-cascade-layers/.npmignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.tsbuildinfo*
|
||||||
|
tsconfig*
|
||||||
|
__tests__
|
7
packages/docusaurus-plugin-css-cascade-layers/README.md
Normal file
7
packages/docusaurus-plugin-css-cascade-layers/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# `@docusaurus/plugin-css-cascade-layers`
|
||||||
|
|
||||||
|
CSS Cascade Layer plugin for Docusaurus
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See [plugin-css-cascade-layers documentation](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-css-cascade-layers).
|
29
packages/docusaurus-plugin-css-cascade-layers/package.json
Normal file
29
packages/docusaurus-plugin-css-cascade-layers/package.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/plugin-css-cascade-layers",
|
||||||
|
"version": "3.7.0",
|
||||||
|
"description": "CSS Cascade Layer plugin for Docusaurus.",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"types": "lib/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --build",
|
||||||
|
"watch": "tsc --build --watch"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/facebook/docusaurus.git",
|
||||||
|
"directory": "packages/docusaurus-plugin-css-cascade-layers"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@docusaurus/core": "3.7.0",
|
||||||
|
"@docusaurus/types": "3.7.0",
|
||||||
|
"@docusaurus/utils-validation": "3.7.0",
|
||||||
|
"tslib": "^2.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
generateLayersDeclaration,
|
||||||
|
findLayer,
|
||||||
|
isValidLayerName,
|
||||||
|
} from '../layers';
|
||||||
|
import type {PluginOptions} from '../options';
|
||||||
|
|
||||||
|
describe('isValidLayerName', () => {
|
||||||
|
it('accepts valid names', () => {
|
||||||
|
expect(isValidLayerName('layer1')).toBe(true);
|
||||||
|
expect(isValidLayerName('layer1.layer2')).toBe(true);
|
||||||
|
expect(isValidLayerName('layer-1.layer_2.layer3')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects layer with coma', () => {
|
||||||
|
expect(isValidLayerName('lay,er1')).toBe(false);
|
||||||
|
});
|
||||||
|
it('rejects layer with space', () => {
|
||||||
|
expect(isValidLayerName('lay er1')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateLayersDeclaration', () => {
|
||||||
|
it('for list of layers', () => {
|
||||||
|
expect(generateLayersDeclaration(['layer1', 'layer2'])).toBe(
|
||||||
|
'@layer layer1, layer2;',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for empty list of layers', () => {
|
||||||
|
// Not useful to generate it, but still valid CSS anyway
|
||||||
|
expect(generateLayersDeclaration([])).toBe('@layer ;');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findLayer', () => {
|
||||||
|
const inputFilePath = 'filePath';
|
||||||
|
|
||||||
|
function testFor(layers: PluginOptions['layers']) {
|
||||||
|
return findLayer(inputFilePath, Object.entries(layers));
|
||||||
|
}
|
||||||
|
|
||||||
|
it('for empty layers', () => {
|
||||||
|
expect(testFor({})).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for single matching layer', () => {
|
||||||
|
expect(testFor({layer: (filePath) => filePath === inputFilePath})).toBe(
|
||||||
|
'layer',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for single non-matching layer', () => {
|
||||||
|
expect(
|
||||||
|
testFor({layer: (filePath) => filePath !== inputFilePath}),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for multiple matching layers', () => {
|
||||||
|
expect(
|
||||||
|
testFor({
|
||||||
|
layer1: (filePath) => filePath === inputFilePath,
|
||||||
|
layer2: (filePath) => filePath === inputFilePath,
|
||||||
|
layer3: (filePath) => filePath === inputFilePath,
|
||||||
|
}),
|
||||||
|
).toBe('layer1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for multiple non-matching layers', () => {
|
||||||
|
expect(
|
||||||
|
testFor({
|
||||||
|
layer1: (filePath) => filePath !== inputFilePath,
|
||||||
|
layer2: (filePath) => filePath !== inputFilePath,
|
||||||
|
layer3: (filePath) => filePath !== inputFilePath,
|
||||||
|
}),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for multiple mixed matching layers', () => {
|
||||||
|
expect(
|
||||||
|
testFor({
|
||||||
|
layer1: (filePath) => filePath !== inputFilePath,
|
||||||
|
layer2: (filePath) => filePath === inputFilePath,
|
||||||
|
layer3: (filePath) => filePath !== inputFilePath,
|
||||||
|
layer4: (filePath) => filePath === inputFilePath,
|
||||||
|
}),
|
||||||
|
).toBe('layer2');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,105 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||||
|
import {
|
||||||
|
validateOptions,
|
||||||
|
type PluginOptions,
|
||||||
|
type Options,
|
||||||
|
DEFAULT_OPTIONS,
|
||||||
|
} from '../options';
|
||||||
|
import type {Validate} from '@docusaurus/types';
|
||||||
|
|
||||||
|
function testValidateOptions(options: Options) {
|
||||||
|
return validateOptions({
|
||||||
|
validate: normalizePluginOptions as Validate<Options, PluginOptions>,
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('validateOptions', () => {
|
||||||
|
it('accepts undefined options', () => {
|
||||||
|
// @ts-expect-error: should error
|
||||||
|
expect(testValidateOptions(undefined)).toEqual(DEFAULT_OPTIONS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts empty options', () => {
|
||||||
|
expect(testValidateOptions({})).toEqual(DEFAULT_OPTIONS);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('layers', () => {
|
||||||
|
it('accepts empty layers', () => {
|
||||||
|
expect(testValidateOptions({layers: {}})).toEqual({
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
layers: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts undefined layers', () => {
|
||||||
|
const config: Options = {
|
||||||
|
layers: undefined,
|
||||||
|
};
|
||||||
|
expect(testValidateOptions(config)).toEqual(DEFAULT_OPTIONS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts custom layers', () => {
|
||||||
|
const config: Options = {
|
||||||
|
layers: {
|
||||||
|
layer1: (filePath: string) => {
|
||||||
|
return !!filePath;
|
||||||
|
},
|
||||||
|
layer2: (filePath: string) => {
|
||||||
|
return !!filePath;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(testValidateOptions(config)).toEqual({
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
layers: config.layers,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects layer with bad name', () => {
|
||||||
|
const config: Options = {
|
||||||
|
layers: {
|
||||||
|
'layer 1': (filePath) => !!filePath,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(() =>
|
||||||
|
testValidateOptions(config),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(`""layers.layer 1" is not allowed"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects layer with bad value', () => {
|
||||||
|
const config: Options = {
|
||||||
|
layers: {
|
||||||
|
// @ts-expect-error: should error
|
||||||
|
layer1: 'bad value',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(() =>
|
||||||
|
testValidateOptions(config),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`""layers.layer1" must be of type function"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects layer with bad function arity', () => {
|
||||||
|
const config: Options = {
|
||||||
|
layers: {
|
||||||
|
// @ts-expect-error: should error
|
||||||
|
layer1: () => {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(() =>
|
||||||
|
testValidateOptions(config),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`""layers.layer1" must have an arity of 1"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
68
packages/docusaurus-plugin-css-cascade-layers/src/index.ts
Normal file
68
packages/docusaurus-plugin-css-cascade-layers/src/index.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
import {PostCssPluginWrapInLayer} from './postCssPlugin';
|
||||||
|
import {generateLayersDeclaration} from './layers';
|
||||||
|
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||||
|
import type {PluginOptions, Options} from './options';
|
||||||
|
|
||||||
|
const PluginName = 'docusaurus-plugin-css-cascade-layers';
|
||||||
|
|
||||||
|
const LayersDeclarationModule = 'layers.css';
|
||||||
|
|
||||||
|
function getLayersDeclarationPath(
|
||||||
|
context: LoadContext,
|
||||||
|
options: PluginOptions,
|
||||||
|
) {
|
||||||
|
const {generatedFilesDir} = context;
|
||||||
|
const pluginId = options.id;
|
||||||
|
if (pluginId !== 'default') {
|
||||||
|
// Since it's only possible to declare a single layer order
|
||||||
|
// using this plugin twice doesn't really make sense
|
||||||
|
throw new Error(
|
||||||
|
'The CSS Cascade Layers plugin does not support multiple instances.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return path.join(
|
||||||
|
generatedFilesDir,
|
||||||
|
PluginName,
|
||||||
|
pluginId,
|
||||||
|
LayersDeclarationModule,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function pluginCssCascadeLayers(
|
||||||
|
context: LoadContext,
|
||||||
|
options: PluginOptions,
|
||||||
|
): Plugin | null {
|
||||||
|
const layersDeclarationPath = getLayersDeclarationPath(context, options);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: PluginName,
|
||||||
|
|
||||||
|
getClientModules() {
|
||||||
|
return [layersDeclarationPath];
|
||||||
|
},
|
||||||
|
|
||||||
|
async contentLoaded({actions}) {
|
||||||
|
await actions.createData(
|
||||||
|
LayersDeclarationModule,
|
||||||
|
generateLayersDeclaration(Object.keys(options.layers)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
configurePostCss(postCssOptions) {
|
||||||
|
postCssOptions.plugins.push(PostCssPluginWrapInLayer(options));
|
||||||
|
return postCssOptions;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {validateOptions} from './options';
|
||||||
|
|
||||||
|
export type {PluginOptions, Options};
|
27
packages/docusaurus-plugin-css-cascade-layers/src/layers.ts
Normal file
27
packages/docusaurus-plugin-css-cascade-layers/src/layers.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type LayerEntry = [string, (filePath: string) => boolean];
|
||||||
|
|
||||||
|
export function isValidLayerName(layer: string): boolean {
|
||||||
|
// TODO improve validation rule to match spec, not high priority
|
||||||
|
return !layer.includes(',') && !layer.includes(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateLayersDeclaration(layers: string[]): string {
|
||||||
|
return `@layer ${layers.join(', ')};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findLayer(
|
||||||
|
filePath: string,
|
||||||
|
layers: LayerEntry[],
|
||||||
|
): string | undefined {
|
||||||
|
// Using find() => layers order matter
|
||||||
|
// The first layer that matches is used in priority even if others match too
|
||||||
|
const layerEntry = layers.find((layer) => layer[1](filePath));
|
||||||
|
return layerEntry?.[0]; // return layer name
|
||||||
|
}
|
87
packages/docusaurus-plugin-css-cascade-layers/src/options.ts
Normal file
87
packages/docusaurus-plugin-css-cascade-layers/src/options.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
import {Joi} from '@docusaurus/utils-validation';
|
||||||
|
import {isValidLayerName} from './layers';
|
||||||
|
import type {OptionValidationContext} from '@docusaurus/types';
|
||||||
|
|
||||||
|
export type PluginOptions = {
|
||||||
|
id: string; // plugin id
|
||||||
|
layers: Record<string, (filePath: string) => boolean>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Options = {
|
||||||
|
layers?: PluginOptions['layers'];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Not ideal to compute layers using "filePath.includes()"
|
||||||
|
// But this is mostly temporary until we add first-class layers everywhere
|
||||||
|
function layerFor(...params: string[]) {
|
||||||
|
return (filePath: string) => params.some((p) => filePath.includes(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object order matters, it defines the layer order
|
||||||
|
export const DEFAULT_LAYERS: PluginOptions['layers'] = {
|
||||||
|
'docusaurus.infima': layerFor('node_modules/infima/dist'),
|
||||||
|
'docusaurus.theme-common': layerFor(
|
||||||
|
'packages/docusaurus-theme-common/lib',
|
||||||
|
'node_modules/@docusaurus/theme-common/lib',
|
||||||
|
),
|
||||||
|
'docusaurus.theme-classic': layerFor(
|
||||||
|
'packages/docusaurus-theme-classic/lib',
|
||||||
|
'node_modules/@docusaurus/theme-classic/lib',
|
||||||
|
),
|
||||||
|
'docusaurus.core': layerFor(
|
||||||
|
'packages/docusaurus/lib',
|
||||||
|
'node_modules/@docusaurus/core/lib',
|
||||||
|
),
|
||||||
|
'docusaurus.plugin-debug': layerFor(
|
||||||
|
'packages/docusaurus-plugin-debug/lib',
|
||||||
|
'node_modules/@docusaurus/plugin-debug/lib',
|
||||||
|
),
|
||||||
|
'docusaurus.theme-mermaid': layerFor(
|
||||||
|
'packages/docusaurus-theme-mermaid/lib',
|
||||||
|
'node_modules/@docusaurus/theme-mermaid/lib',
|
||||||
|
),
|
||||||
|
'docusaurus.theme-live-codeblock': layerFor(
|
||||||
|
'packages/docusaurus-theme-live-codeblock/lib',
|
||||||
|
'node_modules/@docusaurus/theme-live-codeblock/lib',
|
||||||
|
),
|
||||||
|
'docusaurus.theme-search-algolia.docsearch': layerFor(
|
||||||
|
'node_modules/@docsearch/css/dist',
|
||||||
|
),
|
||||||
|
'docusaurus.theme-search-algolia': layerFor(
|
||||||
|
'packages/docusaurus-theme-search-algolia/lib',
|
||||||
|
'node_modules/@docusaurus/theme-search-algolia/lib',
|
||||||
|
),
|
||||||
|
// docusaurus.website layer ? (declare it, even if empty?)
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_OPTIONS: Partial<PluginOptions> = {
|
||||||
|
id: 'default',
|
||||||
|
layers: DEFAULT_LAYERS,
|
||||||
|
};
|
||||||
|
|
||||||
|
const pluginOptionsSchema = Joi.object<PluginOptions>({
|
||||||
|
layers: Joi.object()
|
||||||
|
.pattern(
|
||||||
|
Joi.custom((val, helpers) => {
|
||||||
|
if (!isValidLayerName(val)) {
|
||||||
|
return helpers.error('any.invalid');
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}),
|
||||||
|
Joi.function().arity(1).required(),
|
||||||
|
)
|
||||||
|
.default(DEFAULT_LAYERS),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function validateOptions({
|
||||||
|
validate,
|
||||||
|
options,
|
||||||
|
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||||
|
return validate(pluginOptionsSchema, options);
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {findLayer} from './layers';
|
||||||
|
import type {Root, PluginCreator} from 'postcss';
|
||||||
|
import type {PluginOptions} from './options';
|
||||||
|
|
||||||
|
function wrapCssRootInLayer(root: Root, layer: string): void {
|
||||||
|
const rootBefore = root.clone();
|
||||||
|
root.removeAll();
|
||||||
|
root.append({
|
||||||
|
type: 'atrule',
|
||||||
|
name: 'layer',
|
||||||
|
params: layer,
|
||||||
|
nodes: rootBefore.nodes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PostCssPluginWrapInLayer: PluginCreator<{
|
||||||
|
layers: PluginOptions['layers'];
|
||||||
|
}> = (options) => {
|
||||||
|
if (!options) {
|
||||||
|
throw new Error('PostCssPluginWrapInLayer options are mandatory');
|
||||||
|
}
|
||||||
|
const layers = Object.entries(options.layers);
|
||||||
|
return {
|
||||||
|
postcssPlugin: 'postcss-wrap-in-layer',
|
||||||
|
Once(root) {
|
||||||
|
const filePath = root.source?.input.file;
|
||||||
|
if (!filePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const layer = findLayer(filePath, layers);
|
||||||
|
if (layer) {
|
||||||
|
wrapCssRootInLayer(root, layer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
PostCssPluginWrapInLayer.postcss = true;
|
8
packages/docusaurus-plugin-css-cascade-layers/src/types.d.ts
vendored
Normal file
8
packages/docusaurus-plugin-css-cascade-layers/src/types.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="@docusaurus/module-type-aliases" />
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": false
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["**/__tests__/**"]
|
||||||
|
}
|
|
@ -19,6 +19,7 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "3.7.0",
|
"@docusaurus/core": "3.7.0",
|
||||||
|
"@docusaurus/plugin-css-cascade-layers": "3.7.0",
|
||||||
"@docusaurus/plugin-content-blog": "3.7.0",
|
"@docusaurus/plugin-content-blog": "3.7.0",
|
||||||
"@docusaurus/plugin-content-docs": "3.7.0",
|
"@docusaurus/plugin-content-docs": "3.7.0",
|
||||||
"@docusaurus/plugin-content-pages": "3.7.0",
|
"@docusaurus/plugin-content-pages": "3.7.0",
|
||||||
|
|
|
@ -62,6 +62,13 @@ export default function preset(
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugins: PluginConfig[] = [];
|
const plugins: PluginConfig[] = [];
|
||||||
|
|
||||||
|
// TODO Docusaurus v4: temporary due to the opt-in flag
|
||||||
|
// In v4 we'd like to use layers everywhere natively
|
||||||
|
if (siteConfig.future.v4.useCssCascadeLayers) {
|
||||||
|
plugins.push(makePluginConfig('@docusaurus/plugin-css-cascade-layers'));
|
||||||
|
}
|
||||||
|
|
||||||
if (docs !== false) {
|
if (docs !== false) {
|
||||||
plugins.push(makePluginConfig('@docusaurus/plugin-content-docs', docs));
|
plugins.push(makePluginConfig('@docusaurus/plugin-content-docs', docs));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,3 +13,11 @@ Hide color mode toggle in small viewports
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Restore some Infima style that broke with CSS Cascade Layers
|
||||||
|
See https://github.com/facebook/docusaurus/pull/11142
|
||||||
|
*/
|
||||||
|
:global(.navbar__items--right) > :last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
1
packages/docusaurus-types/src/config.d.ts
vendored
1
packages/docusaurus-types/src/config.d.ts
vendored
|
@ -136,6 +136,7 @@ export type FasterConfig = {
|
||||||
|
|
||||||
export type FutureV4Config = {
|
export type FutureV4Config = {
|
||||||
removeLegacyPostBuildHeadAttribute: boolean;
|
removeLegacyPostBuildHeadAttribute: boolean;
|
||||||
|
useCssCascadeLayers: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FutureConfig = {
|
export type FutureConfig = {
|
||||||
|
|
|
@ -25,6 +25,7 @@ exports[`loadSiteConfig website with .cjs siteConfig 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -99,6 +100,7 @@ exports[`loadSiteConfig website with ts + js config 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -173,6 +175,7 @@ exports[`loadSiteConfig website with valid JS CJS config 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -247,6 +250,7 @@ exports[`loadSiteConfig website with valid JS ESM config 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -321,6 +325,7 @@ exports[`loadSiteConfig website with valid TypeScript CJS config 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -395,6 +400,7 @@ exports[`loadSiteConfig website with valid TypeScript ESM config 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -469,6 +475,7 @@ exports[`loadSiteConfig website with valid async config 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -545,6 +552,7 @@ exports[`loadSiteConfig website with valid async config creator function 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -621,6 +629,7 @@ exports[`loadSiteConfig website with valid config creator function 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
@ -700,6 +709,7 @@ exports[`loadSiteConfig website with valid siteConfig 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
|
|
@ -99,6 +99,7 @@ exports[`load loads props for site with custom i18n path 1`] = `
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"removeLegacyPostBuildHeadAttribute": false,
|
"removeLegacyPostBuildHeadAttribute": false,
|
||||||
|
"useCssCascadeLayers": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"headTags": [],
|
"headTags": [],
|
||||||
|
|
|
@ -50,6 +50,7 @@ describe('normalizeConfig', () => {
|
||||||
future: {
|
future: {
|
||||||
v4: {
|
v4: {
|
||||||
removeLegacyPostBuildHeadAttribute: true,
|
removeLegacyPostBuildHeadAttribute: true,
|
||||||
|
useCssCascadeLayers: true,
|
||||||
},
|
},
|
||||||
experimental_faster: {
|
experimental_faster: {
|
||||||
swcJsLoader: true,
|
swcJsLoader: true,
|
||||||
|
@ -754,6 +755,7 @@ describe('future', () => {
|
||||||
const future: DocusaurusConfig['future'] = {
|
const future: DocusaurusConfig['future'] = {
|
||||||
v4: {
|
v4: {
|
||||||
removeLegacyPostBuildHeadAttribute: true,
|
removeLegacyPostBuildHeadAttribute: true,
|
||||||
|
useCssCascadeLayers: true,
|
||||||
},
|
},
|
||||||
experimental_faster: {
|
experimental_faster: {
|
||||||
swcJsLoader: true,
|
swcJsLoader: true,
|
||||||
|
@ -1861,6 +1863,7 @@ describe('future', () => {
|
||||||
it('accepts v4 - full', () => {
|
it('accepts v4 - full', () => {
|
||||||
const v4: FutureV4Config = {
|
const v4: FutureV4Config = {
|
||||||
removeLegacyPostBuildHeadAttribute: true,
|
removeLegacyPostBuildHeadAttribute: true,
|
||||||
|
useCssCascadeLayers: true,
|
||||||
};
|
};
|
||||||
expect(
|
expect(
|
||||||
normalizeConfig({
|
normalizeConfig({
|
||||||
|
@ -1976,5 +1979,80 @@ describe('future', () => {
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('useCssCascadeLayers', () => {
|
||||||
|
it('accepts - undefined', () => {
|
||||||
|
const v4: Partial<FutureV4Config> = {
|
||||||
|
useCssCascadeLayers: undefined,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
normalizeConfig({
|
||||||
|
future: {
|
||||||
|
v4,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual(v4Containing({useCssCascadeLayers: false}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts - true', () => {
|
||||||
|
const v4: Partial<FutureV4Config> = {
|
||||||
|
useCssCascadeLayers: true,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
normalizeConfig({
|
||||||
|
future: {
|
||||||
|
v4,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual(v4Containing({useCssCascadeLayers: true}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts - false', () => {
|
||||||
|
const v4: Partial<FutureV4Config> = {
|
||||||
|
useCssCascadeLayers: false,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
normalizeConfig({
|
||||||
|
future: {
|
||||||
|
v4,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual(v4Containing({useCssCascadeLayers: false}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects - null', () => {
|
||||||
|
const v4: Partial<FutureV4Config> = {
|
||||||
|
// @ts-expect-error: invalid
|
||||||
|
useCssCascadeLayers: 42,
|
||||||
|
};
|
||||||
|
expect(() =>
|
||||||
|
normalizeConfig({
|
||||||
|
future: {
|
||||||
|
v4,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(`
|
||||||
|
""future.v4.useCssCascadeLayers" must be a boolean
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects - number', () => {
|
||||||
|
const v4: Partial<FutureV4Config> = {
|
||||||
|
// @ts-expect-error: invalid
|
||||||
|
useCssCascadeLayers: 42,
|
||||||
|
};
|
||||||
|
expect(() =>
|
||||||
|
normalizeConfig({
|
||||||
|
future: {
|
||||||
|
v4,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(`
|
||||||
|
""future.v4.useCssCascadeLayers" must be a boolean
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -68,11 +68,13 @@ export const DEFAULT_FASTER_CONFIG_TRUE: FasterConfig = {
|
||||||
|
|
||||||
export const DEFAULT_FUTURE_V4_CONFIG: FutureV4Config = {
|
export const DEFAULT_FUTURE_V4_CONFIG: FutureV4Config = {
|
||||||
removeLegacyPostBuildHeadAttribute: false,
|
removeLegacyPostBuildHeadAttribute: false,
|
||||||
|
useCssCascadeLayers: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// When using the "v4: true" shortcut
|
// When using the "v4: true" shortcut
|
||||||
export const DEFAULT_FUTURE_V4_CONFIG_TRUE: FutureV4Config = {
|
export const DEFAULT_FUTURE_V4_CONFIG_TRUE: FutureV4Config = {
|
||||||
removeLegacyPostBuildHeadAttribute: true,
|
removeLegacyPostBuildHeadAttribute: true,
|
||||||
|
useCssCascadeLayers: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_FUTURE_CONFIG: FutureConfig = {
|
export const DEFAULT_FUTURE_CONFIG: FutureConfig = {
|
||||||
|
@ -270,6 +272,9 @@ const FUTURE_V4_SCHEMA = Joi.alternatives()
|
||||||
removeLegacyPostBuildHeadAttribute: Joi.boolean().default(
|
removeLegacyPostBuildHeadAttribute: Joi.boolean().default(
|
||||||
DEFAULT_FUTURE_V4_CONFIG.removeLegacyPostBuildHeadAttribute,
|
DEFAULT_FUTURE_V4_CONFIG.removeLegacyPostBuildHeadAttribute,
|
||||||
),
|
),
|
||||||
|
useCssCascadeLayers: Joi.boolean().default(
|
||||||
|
DEFAULT_FUTURE_V4_CONFIG.useCssCascadeLayers,
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
Joi.boolean()
|
Joi.boolean()
|
||||||
.required()
|
.required()
|
||||||
|
|
|
@ -109,6 +109,7 @@ Héctor
|
||||||
héllô
|
héllô
|
||||||
IANAD
|
IANAD
|
||||||
Infima
|
Infima
|
||||||
|
infima
|
||||||
inlines
|
inlines
|
||||||
interactiveness
|
interactiveness
|
||||||
Interpolatable
|
Interpolatable
|
||||||
|
|
|
@ -41,3 +41,4 @@ import Readme from "../README.mdx"
|
||||||
- [Analytics](/tests/pages/analytics)
|
- [Analytics](/tests/pages/analytics)
|
||||||
- [History tests](/tests/pages/history-tests)
|
- [History tests](/tests/pages/history-tests)
|
||||||
- [Embeds](/tests/pages/embeds)
|
- [Embeds](/tests/pages/embeds)
|
||||||
|
- [Style Isolation tests](/tests/pages/style-isolation)
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.exampleContainer {
|
||||||
|
border: solid thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isolated:not(#a#b) {
|
||||||
|
&,
|
||||||
|
* {
|
||||||
|
@layer docusaurus {
|
||||||
|
all: revert-layer;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Yes, unfortunately we need to revert sub-layers one by one
|
||||||
|
See https://bsky.app/profile/sebastienlorber.com/post/3lpqzuxat6s2v
|
||||||
|
*/
|
||||||
|
@layer docusaurus.infima {
|
||||||
|
all: revert-layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
174
website/_dogfooding/_pages tests/style-isolation/index.tsx
Normal file
174
website/_dogfooding/_pages tests/style-isolation/index.tsx
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, {type ReactNode} from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
|
import Layout from '@theme/Layout';
|
||||||
|
import Heading from '@theme/Heading';
|
||||||
|
|
||||||
|
import styles from './index.module.css';
|
||||||
|
|
||||||
|
/* eslint-disable @docusaurus/prefer-docusaurus-heading */
|
||||||
|
|
||||||
|
function ExampleContainer({
|
||||||
|
isolated,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
isolated?: boolean;
|
||||||
|
children: ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
styles.exampleContainer,
|
||||||
|
isolated ? styles.isolated : undefined,
|
||||||
|
)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ExampleRow({name, children}: {name: string; children: ReactNode}) {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td>{name}</td>
|
||||||
|
<td>
|
||||||
|
<ExampleContainer>{children}</ExampleContainer>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<ExampleContainer isolated>{children}</ExampleContainer>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ExamplesTable() {
|
||||||
|
return (
|
||||||
|
<table className="table-auto border-collapse border border-gray-300">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Example</th>
|
||||||
|
<th>Normal</th>
|
||||||
|
<th>Isolated</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<ExampleRow name="h1">
|
||||||
|
<h1>title</h1>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="p">
|
||||||
|
<p>text</p>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="a">
|
||||||
|
{/* eslint-disable-next-line */}
|
||||||
|
<a href="https://example.com">link</a>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="code">
|
||||||
|
<code>code</code>
|
||||||
|
</ExampleRow>
|
||||||
|
<ExampleRow name="pre > code">
|
||||||
|
<pre>
|
||||||
|
<code>code</code>
|
||||||
|
</pre>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="blockquote">
|
||||||
|
<blockquote>some text</blockquote>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="button">
|
||||||
|
{/* eslint-disable-next-line */}
|
||||||
|
<button>button</button>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="ul">
|
||||||
|
<ul>
|
||||||
|
<li>item1</li>
|
||||||
|
<li>item2</li>
|
||||||
|
</ul>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="ol">
|
||||||
|
<ol>
|
||||||
|
<li>item1</li>
|
||||||
|
<li>item2</li>
|
||||||
|
</ol>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="kbd">
|
||||||
|
<kbd>kbd</kbd>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="shadow">
|
||||||
|
<div className="shadow--tl">shadow (KO)</div>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="table">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Col1</th>
|
||||||
|
<th>Col2</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Cell1</td>
|
||||||
|
<td>Cell2</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Cell3</td>
|
||||||
|
<td>Cell3</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="Infima button primary">
|
||||||
|
{/* eslint-disable-next-line */}
|
||||||
|
<button className="button button--primary">button</button>
|
||||||
|
</ExampleRow>
|
||||||
|
|
||||||
|
<ExampleRow name="Infima alert danger">
|
||||||
|
<div className="alert alert--danger">danger</div>
|
||||||
|
</ExampleRow>
|
||||||
|
<ExampleRow name="Infima badge success">
|
||||||
|
<div className="badge badge--success">success</div>
|
||||||
|
</ExampleRow>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function StyleIsolation(): ReactNode {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<main
|
||||||
|
style={{padding: 30}}
|
||||||
|
className="markdown" // class added on purpose, creates extra pollution
|
||||||
|
>
|
||||||
|
<Heading as="h1">Style Isolation tests</Heading>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This shows how to isolate your components from Docusaurus global
|
||||||
|
styles. A workaround for{' '}
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/facebook/docusaurus/issues/6032">
|
||||||
|
this issue
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
<ExamplesTable />
|
||||||
|
</main>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -199,6 +199,7 @@ export default {
|
||||||
future: {
|
future: {
|
||||||
v4: {
|
v4: {
|
||||||
removeLegacyPostBuildHeadAttribute: true,
|
removeLegacyPostBuildHeadAttribute: true,
|
||||||
|
useCssCascadeLayers: true,
|
||||||
},
|
},
|
||||||
experimental_faster: {
|
experimental_faster: {
|
||||||
swcJsLoader: true,
|
swcJsLoader: true,
|
||||||
|
@ -221,6 +222,7 @@ export default {
|
||||||
|
|
||||||
- `v4`: Permits to opt-in for upcoming Docusaurus v4 breaking changes and features, to prepare your site in advance for this new version. Use `true` as a shorthand to enable all the flags.
|
- `v4`: Permits to opt-in for upcoming Docusaurus v4 breaking changes and features, to prepare your site in advance for this new version. Use `true` as a shorthand to enable all the flags.
|
||||||
- [`removeLegacyPostBuildHeadAttribute`](https://github.com/facebook/docusaurus/pull/10435): Removes the legacy `plugin.postBuild({head})` API that prevents us from applying useful SSG optimizations ([explanations](https://github.com/facebook/docusaurus/pull/10850)).
|
- [`removeLegacyPostBuildHeadAttribute`](https://github.com/facebook/docusaurus/pull/10435): Removes the legacy `plugin.postBuild({head})` API that prevents us from applying useful SSG optimizations ([explanations](https://github.com/facebook/docusaurus/pull/10850)).
|
||||||
|
- [`useCssCascadeLayers`](https://github.com/facebook/docusaurus/pull/11142): This enables the [Docusaurus CSS Cascade Layers plugin](./plugins/plugin-css-cascade-layers.mdx) with pre-configured layers that we plan to apply by default for Docusaurus v4.
|
||||||
- `experimental_faster`: An object containing feature flags to make the Docusaurus build faster. This requires adding the `@docusaurus/faster` package to your site's dependencies. Use `true` as a shorthand to enable all flags. Read more on the [Docusaurus Faster](https://github.com/facebook/docusaurus/issues/10556) issue. Available feature flags:
|
- `experimental_faster`: An object containing feature flags to make the Docusaurus build faster. This requires adding the `@docusaurus/faster` package to your site's dependencies. Use `true` as a shorthand to enable all flags. Read more on the [Docusaurus Faster](https://github.com/facebook/docusaurus/issues/10556) issue. Available feature flags:
|
||||||
- [`swcJsLoader`](https://github.com/facebook/docusaurus/pull/10435): Use [SWC](https://swc.rs/) to transpile JS (instead of [Babel](https://babeljs.io/)).
|
- [`swcJsLoader`](https://github.com/facebook/docusaurus/pull/10435): Use [SWC](https://swc.rs/) to transpile JS (instead of [Babel](https://babeljs.io/)).
|
||||||
- [`swcJsMinimizer`](https://github.com/facebook/docusaurus/pull/10441): Use [SWC](https://swc.rs/) to minify JS (instead of [Terser](https://github.com/terser/terser)).
|
- [`swcJsMinimizer`](https://github.com/facebook/docusaurus/pull/10441): Use [SWC](https://swc.rs/) to minify JS (instead of [Terser](https://github.com/terser/terser)).
|
||||||
|
|
|
@ -31,3 +31,4 @@ These plugins will add a useful behavior to your Docusaurus site.
|
||||||
- [@docusaurus/plugin-google-analytics](./plugin-google-analytics.mdx)
|
- [@docusaurus/plugin-google-analytics](./plugin-google-analytics.mdx)
|
||||||
- [@docusaurus/plugin-google-gtag](./plugin-google-gtag.mdx)
|
- [@docusaurus/plugin-google-gtag](./plugin-google-gtag.mdx)
|
||||||
- [@docusaurus/plugin-google-tag-manager](./plugin-google-tag-manager.mdx)
|
- [@docusaurus/plugin-google-tag-manager](./plugin-google-tag-manager.mdx)
|
||||||
|
- [@docusaurus/plugin-css-cascade-layers](./plugin-css-cascade-layers.mdx) u
|
||||||
|
|
95
website/docs/api/plugins/plugin-css-cascade-layers.mdx
Normal file
95
website/docs/api/plugins/plugin-css-cascade-layers.mdx
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 9
|
||||||
|
slug: /api/plugins/@docusaurus/plugin-css-cascade-layers
|
||||||
|
---
|
||||||
|
|
||||||
|
# 📦 plugin-css-cascade-layers
|
||||||
|
|
||||||
|
import APITable from '@site/src/components/APITable';
|
||||||
|
|
||||||
|
:::caution Experimental
|
||||||
|
|
||||||
|
This plugin is mostly designed to be used internally by the classic preset through the [Docusaurus `future.v4.useCssCascadeLayers` flag](../docusaurus.config.js.mdx#future), although it can also be used as a standalone plugin. Please [let us know here](https://github.com/facebook/docusaurus/pull/11142) if you have a use case for it and help us design an API that makes sense for the future of Docusaurus.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
A plugin for wrapping CSS modules of your Docusaurus site in [CSS Cascade Layers](https://css-tricks.com/css-cascade-layers/). This modern CSS feature is widely supported by all browsers. It allows grouping CSS rules in layers of specificity and gives you more control over the CSS cascade.
|
||||||
|
|
||||||
|
Use this plugin to:
|
||||||
|
|
||||||
|
- apply a top-level `@layer myLayer { ... }` block rule around any CSS module, including un-layered third-party CSS.
|
||||||
|
- define an explicit layer ordering
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
To use this plugin properly, it's recommended to have a solid understanding of [CSS Cascade Layers](https://css-tricks.com/css-cascade-layers/), the [CSS Cascade](https://developer.mozilla.org/docs/Web/CSS/CSS_cascade/Cascade) and [specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity).
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Installation {#installation}
|
||||||
|
|
||||||
|
```bash npm2yarn
|
||||||
|
npm install --save @docusaurus/plugin-css-cascade-layers
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
|
||||||
|
If you use the preset `@docusaurus/preset-classic`, this plugin is automatically configured for you with the [`siteConfig.future.v4.useCssCascadeLayers`](../docusaurus.config.js.mdx#future) flag.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Configuration {#configuration}
|
||||||
|
|
||||||
|
Accepted fields:
|
||||||
|
|
||||||
|
```mdx-code-block
|
||||||
|
<APITable>
|
||||||
|
```
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `layers` | `Layers` | **Built-in layers** | An object representing all the CSS cascade layers you want to use, and whether the layer should be applied to a given file path. See examples and types below. |
|
||||||
|
|
||||||
|
```mdx-code-block
|
||||||
|
</APITable>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Types {#types}
|
||||||
|
|
||||||
|
#### `Layers` {#EditUrlFunction}
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Layers = Record<
|
||||||
|
string, // layer name
|
||||||
|
(filePath: string) => boolean // layer matcher
|
||||||
|
>;
|
||||||
|
```
|
||||||
|
|
||||||
|
The `layers` object is defined by:
|
||||||
|
|
||||||
|
- key: the name of a layer
|
||||||
|
- value: a function to define if a given CSS module file should be in that layer
|
||||||
|
|
||||||
|
:::caution Order matters
|
||||||
|
|
||||||
|
The object order matters:
|
||||||
|
|
||||||
|
- the keys order defines an explicit CSS layer order
|
||||||
|
- when multiple layers match a file path, only the first layer will apply
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Example configuration {#ex-config}
|
||||||
|
|
||||||
|
You can configure this plugin through plugin options.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const options = {
|
||||||
|
layers: {
|
||||||
|
'docusaurus.infima': (filePath) =>
|
||||||
|
filePath.includes('/node_modules/infima/dist'),
|
||||||
|
'docusaurus.theme-classic': (filePath) =>
|
||||||
|
filePath.includes('/node_modules/@docusaurus/theme-classic/lib'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
Loading…
Add table
Add a link
Reference in a new issue