Skip to content

Commit

Permalink
Support CSS nesting
Browse files Browse the repository at this point in the history
  • Loading branch information
yhatt committed Aug 30, 2024
1 parent 2390a42 commit 5ece467
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 58 deletions.
83 changes: 79 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"jsdoc": "^4.0.3",
"npm-check-updates": "^17.1.0",
"npm-run-all2": "^6.2.2",
"postcss-selector-parser": "^6.1.2",
"prettier": "^3.3.3",
"rimraf": "^6.0.1",
"sass": "1.77.8",
Expand All @@ -88,12 +89,14 @@
"ws": "^8.18.0"
},
"dependencies": {
"@csstools/postcss-is-pseudo-class": "^5.0.0",
"cssesc": "^3.0.0",
"js-yaml": "^4.1.0",
"lodash.kebabcase": "^4.1.1",
"markdown-it": "^14.1.0",
"markdown-it-front-matter": "^0.2.4",
"postcss": "^8.4.41"
"postcss": "^8.4.41",
"postcss-nesting": "^13.0.0"
},
"publishConfig": {
"access": "public"
Expand Down
10 changes: 8 additions & 2 deletions src/marpit.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const defaultOptions = {
anchor: true,
container: marpitContainer,
cssContainerQuery: false,
cssNesting: true,
headingDivider: false,
lang: undefined,
looseYAML: false,
Expand Down Expand Up @@ -73,6 +74,9 @@ class Marpit {
* to enable CSS container query (`@container`). By setting the string or
* string array, you can specify the container name(s) for the CSS
* container.
* @param {boolean} [opts.cssNesting=false] Enable CSS nesting support. If
* enabled, Marpit will try to make flatten the CSS with nested rules
* before rendering, to make it compatible with Marpit preprocessing.
* @param {false|number|number[]} [opts.headingDivider=false] Start a new
* slide page at before of headings. it would apply to headings whose
* larger than or equal to the specified level if a number is given, or
Expand Down Expand Up @@ -128,7 +132,9 @@ class Marpit {
/**
* @type {ThemeSet}
*/
this.themeSet = new ThemeSet()
this.themeSet = new ThemeSet({
cssNesting: this.options.cssNesting,
})

this.applyMarkdownItPlugins(
(() => {
Expand Down Expand Up @@ -267,9 +273,9 @@ class Marpit {
...wrapArray(this.options.container),
...wrapArray(this.options.slideContainer),
],
containerQuery: this.options.cssContainerQuery,
inlineSVG: this.inlineSVGOptions,
printable: this.options.printable,
containerQuery: this.options.cssContainerQuery,
}
}

Expand Down
44 changes: 44 additions & 0 deletions src/postcss/nesting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @module */
import postcssPlugin from '../helpers/postcss_plugin'
import postcssNesting from 'postcss-nesting'
import postcssIsPseudoClass from '@csstools/postcss-is-pseudo-class'

const { Rule: applyPostCSSNesting } = postcssNesting()

const matcher = /:is\((?:section|:root)\b/

export const nesting = postcssPlugin(
'marpit-postcss-nesting',
() => (root, helpers) => {
const rules = []

// Note: Use walk instead of walkRules to include nested rules
root.walk((node) => {
if (node.type !== 'rule') return

rules.push(node)
node.__marpitNestingOriginalSelector = node.selector
})

// Apply postcss-nesting
root.walkRules((rule) => applyPostCSSNesting(rule, helpers))

const { Rule: applyPostCSSIsPseudoClass } = postcssIsPseudoClass({
onComplexSelector: 'warning',
}).prepare()

for (const rule of rules) {
if (
rule.__marpitNestingOriginalSelector !== rule.selector &&
matcher.test(rule.selector)
) {
// Apply postcss-is-pseudo-class only to transformed rules that is
// including `:is() selector starting from `section` element or `:root`
// pseudo-class
applyPostCSSIsPseudoClass(rule, helpers)
}
}
},
)

export default nesting
18 changes: 12 additions & 6 deletions src/theme.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import postcss from 'postcss'
import postcssImportParse from './postcss/import/parse'
import postcssMeta from './postcss/meta'
import postcssNesting from './postcss/nesting'
import { pseudoClass } from './postcss/root/increasing_specificity'
import postcssRootReplace from './postcss/root/replace'
import postcssSectionSize from './postcss/section_size'
Expand All @@ -13,6 +14,7 @@ const absoluteUnits = {
pc: (v) => v * 16,
pt: (v) => (v * 4) / 3,
px: (v) => v,
// q: (v) => (v * 24) / 25.4,
}

const convertToPixel = (value) => {
Expand Down Expand Up @@ -95,16 +97,20 @@ class Theme {
* `@theme` meta comment.
* @param {Object} [opts]
* @param {Object} [opts.metaType] An object for defined types for metadata.
* @param {Object} [opts.cssNesting] Enable support for CSS nesting.
*/
static fromCSS(cssString, opts = {}) {
const metaType = { ...(opts.metaType || {}), ...reservedMetaType }

const { css, result } = postcss([
postcssMeta({ metaType }),
postcssRootReplace({ pseudoClass }),
postcssSectionSize({ preferedPseudoClass: pseudoClass }),
postcssImportParse,
]).process(cssString)
const { css, result } = postcss(
[
!!opts.cssNesting && postcssNesting,
postcssMeta({ metaType }),
postcssRootReplace({ pseudoClass }),
postcssSectionSize({ preferedPseudoClass: pseudoClass }),
postcssImportParse,
].filter((p) => p),
).process(cssString)

if (!opts[skipThemeValidationSymbol] && !result.marpitMeta.theme)
throw new Error('Marpit theme CSS requires @theme meta.')
Expand Down
Loading

0 comments on commit 5ece467

Please sign in to comment.