From b96255669254a7cea9de87041ba43e517e3b2d19 Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Mon, 15 Nov 2021 21:40:34 +0900 Subject: [PATCH 1/6] Implement Marpit PostCSS SVG backdrop plugin --- src/postcss/svg_backdrop.js | 81 ++++++++++++++++++++++++++++++++++++ src/theme_set.js | 2 + test/postcss/section_size.js | 5 +-- test/postcss/svg_backdrop.js | 20 +++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 src/postcss/svg_backdrop.js create mode 100644 test/postcss/svg_backdrop.js diff --git a/src/postcss/svg_backdrop.js b/src/postcss/svg_backdrop.js new file mode 100644 index 00000000..4bd9bb31 --- /dev/null +++ b/src/postcss/svg_backdrop.js @@ -0,0 +1,81 @@ +/** @module */ +import postcssPlugin from '../helpers/postcss_plugin' + +const backdropMatcher = /(?:\b|^)::backdrop$/ + +/** + * Marpit PostCSS SVG backdrop plugin. + * + * Retarget `::backdrop` and `section::backdrop` selector to + * `@media screen { :marpit-container > svg[data-marpit-svg] { .. } }`. It means + * `::backdrop` targets the SVG container in inline SVG mode. + * + * It's useful for setting style of the letterbox and pillarbox in the SVG + * scaled slide. + * + * ```css + * ::backdrop { + * background-color: #448; + * } + * ``` + * + * The original definition will remain to support an original usage of + * `::backdrop`. + * + * The important differences from an original `::backdrop` are following: + * + * - In original spec, `::backdrop` creates a separated layer from the target + * element, but Marpit's `::backdrop` does not. The slide elements still + * become the child of `::backdrop` so setting some properties that are + * inherited may make broken slide rendering. + * - Even if the browser is not fullscreen, `::backdrop` will match to SVG + * container whenever matched to `@media screen` media query. + * + * If concerned to conflict with the style provided by the app, consider to + * disable the selector support by `inlineSVG: { backdrop: false }`. + * + * @see https://developer.mozilla.org/docs/Web/CSS/::backdrop + * @alias module:postcss/svg_backdrop + */ +const plugin = postcssPlugin( + 'marpit-postcss-svg-backdrop', + () => (css, postcss) => { + css.walkRules((rule) => { + const injectSelectors = new Set() + + for (const selector of rule.selectors) { + // Detect pseudo-element (must appear after the simple selectors) + if (!selector.match(backdropMatcher)) continue + + // Detect whether the selector is targeted to section + const delimiterMatched = selector.match(/[.:#[]/) // must match + const target = selector.slice(0, delimiterMatched.index) + + if (target === 'section' || target === '') { + const delimiter = selector.slice(delimiterMatched.index, -10) + injectSelectors.add( + `:marpit-container > svg[data-marpit-svg]${delimiter}` + ) + } + } + + if (injectSelectors.size > 0 && rule.nodes.length > 0) { + rule.parent.insertAfter( + rule, + postcss.atRule({ + name: 'media', + params: 'screen', + nodes: [ + postcss.rule({ + selectors: [...injectSelectors.values()], + nodes: rule.nodes, + }), + ], + }) + ) + } + }) + } +) + +export default plugin diff --git a/src/theme_set.js b/src/theme_set.js index 49127cd2..fd326dd4 100644 --- a/src/theme_set.js +++ b/src/theme_set.js @@ -16,6 +16,7 @@ import postcssRootIncreasingSpecificity, { } from './postcss/root/increasing_specificity' import postcssRem from './postcss/root/rem' import postcssRootReplace from './postcss/root/replace' +import postcssSVGBackdrop from './postcss/svg_backdrop' import Theme from './theme' import scaffold from './theme/scaffold' @@ -284,6 +285,7 @@ class ThemeSet { () => (css) => css.first.before(scaffold.css) ), opts.inlineSVG && postcssAdvancedBackground, + opts.inlineSVG && postcssSVGBackdrop, postcssPagination, postcssRootReplace({ pseudoClass }), postcssRootFontSize, diff --git a/test/postcss/section_size.js b/test/postcss/section_size.js index c1108917..2114f889 100644 --- a/test/postcss/section_size.js +++ b/test/postcss/section_size.js @@ -5,12 +5,11 @@ describe('Marpit PostCSS section size plugin', () => { const run = (input) => postcss([sectionSize()]).process(input, { from: undefined }) - it('adds marpitSectionSize object to result', () => { + it('adds marpitSectionSize object to result', () => run('').then((result) => { expect(result.marpitSectionSize).toBeInstanceOf(Object) expect(result.marpitSectionSize).toStrictEqual({}) - }) - }) + })) it('parses width and height declaration on section selector', () => run('section { width: 123px; height: 456px; }').then((result) => diff --git a/test/postcss/svg_backdrop.js b/test/postcss/svg_backdrop.js new file mode 100644 index 00000000..fce57861 --- /dev/null +++ b/test/postcss/svg_backdrop.js @@ -0,0 +1,20 @@ +import postcss from 'postcss' +import svgBackdrop from '../../src/postcss/svg_backdrop' + +describe('Marpit PostCSS SVG backdrop plugin', () => { + const run = (input) => + postcss([svgBackdrop()]).process(input, { from: undefined }) + + it('appends redirected style for SVG in the container', () => + run('::backdrop { background: white; }').then(({ root }) => { + const [backdrop, redirected] = root.nodes + expect(backdrop.selector).toBe('::backdrop') + + expect(redirected.type).toBe('atrule') + expect(redirected.name).toBe('media') + expect(redirected.params).toBe('screen') + expect(redirected.nodes.toString()).toMatchInlineSnapshot( + `":marpit-container > svg[data-marpit-svg] { background: white; }"` + ) + })) +}) From 696aa09a28e989015c14e2fff14e3ad1d48e6dfb Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Fri, 19 Nov 2021 21:38:34 +0900 Subject: [PATCH 2/6] Allow passing object to inlineSVG constructor option --- src/markdown/background_image.js | 4 ++-- src/markdown/inline_svg.js | 2 +- src/marpit.js | 29 ++++++++++++++++++++++++++--- src/theme_set.js | 10 +++++----- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/markdown/background_image.js b/src/markdown/background_image.js index b8eaac8b..0f2884ef 100644 --- a/src/markdown/background_image.js +++ b/src/markdown/background_image.js @@ -9,11 +9,11 @@ import parse from './background_image/parse' * * Convert image token to backgrounds when the alternate text includes `bg`. * - * When Marpit `inlineSVG` option is `false`, the image will convert to + * When Marpit inline SVG mode is disabled, the image will convert to * `backgroundImage` and `backgroundSize` spot directive. It supports only * single background and resizing by using CSS. * - * When `inlineSVG` option is true, the plugin enables advanced background mode. + * When inline SVG mode is enabled, the plugin enables advanced background mode. * In addition to the basic background implementation, it supports multiple * background images, filters, and split background. * diff --git a/src/markdown/inline_svg.js b/src/markdown/inline_svg.js index 847327af..0d5d64a2 100644 --- a/src/markdown/inline_svg.js +++ b/src/markdown/inline_svg.js @@ -16,7 +16,7 @@ function inlineSVG(md) { 'marpit_directives_parse', 'marpit_inline_svg', (state) => { - if (!marpit.options.inlineSVG || state.inlineMode) return + if (!marpit.inlineSVGOptions.enabled || state.inlineMode) return const { themeSet, lastGlobalDirectives } = marpit const w = themeSet.getThemeProp(lastGlobalDirectives.theme, 'widthPixel') diff --git a/src/marpit.js b/src/marpit.js index 31276e2a..fa062bbd 100644 --- a/src/marpit.js +++ b/src/marpit.js @@ -35,6 +35,14 @@ const defaultOptions = { class Marpit { #markdown = undefined + /** + * @typedef {Object} Marpit~InlineSVGOptions + * @property {boolean} [enabled=true] Whether inline SVG mode is enabled. + * @property {boolean} [backdropSelector=true] Whether `::backdrop` selector + * support is enabled. If enabled, the `::backdrop` CSS selector will + * match to the SVG container element. + */ + /** * Create a Marpit instance. * @@ -54,8 +62,8 @@ class Marpit { * @param {boolean} [opts.printable=true] Make style printable to PDF. * @param {false|Element|Element[]} [opts.slideContainer] Container element(s) * wrapping each slide sections. - * @param {boolean} [opts.inlineSVG=false] Wrap each sections by inline SVG. - * _(Experimental)_ + * @param {boolean|Marpit~InlineSVGOptions} [opts.inlineSVG=false] Wrap each + * slide sections by inline SVG. _(Experimental)_ */ constructor(opts = {}) { /** @@ -225,11 +233,26 @@ class Marpit { ...wrapArray(this.options.container), ...wrapArray(this.options.slideContainer), ], - inlineSVG: this.options.inlineSVG, + inlineSVG: this.inlineSVGOptions, printable: this.options.printable, } } + /** + * @private + * @returns {Marpit~InlineSVGOptions} Options for inline SVG. + */ + get inlineSVGOptions() { + if (typeof this.options.inlineSVG === 'object') { + return this.options.inlineSVG + } + + return { + enabled: !!this.options.inlineSVG, + backdropSelector: true, + } + } + /** * Load the specified markdown-it plugin with given parameters. * diff --git a/src/theme_set.js b/src/theme_set.js index fd326dd4..19acd368 100644 --- a/src/theme_set.js +++ b/src/theme_set.js @@ -237,15 +237,15 @@ class ThemeSet { * @param {Element[]} [opts.containers] Container elements wrapping whole * slide deck. * @param {boolean} [opts.printable] Make style printable to PDF. - * @param {boolean} [opts.inlineSVG] Apply a hierarchy of inline SVG to CSS - * selector by setting `true`. _(Experimental)_ + * @param {Marpit~InlineSVGOptions} [opts.inlineSVG] Apply a hierarchy of + * inline SVG to CSS selector by setting `true`. _(Experimental)_ * @return {string} The converted CSS string. */ pack(name, opts = {}) { const slideElements = [{ tag: 'section' }] const theme = this.get(name, true) - if (opts.inlineSVG) + if (opts.inlineSVG.enabled) slideElements.unshift({ tag: 'svg' }, { tag: 'foreignObject' }) const additionalCSS = (css) => { @@ -284,8 +284,8 @@ class ThemeSet { 'marpit-pack-scaffold', () => (css) => css.first.before(scaffold.css) ), - opts.inlineSVG && postcssAdvancedBackground, - opts.inlineSVG && postcssSVGBackdrop, + opts.inlineSVG.enabled && postcssAdvancedBackground, + opts.inlineSVG.enabled && opts.inlineSVG.backdrop && postcssSVGBackdrop, postcssPagination, postcssRootReplace({ pseudoClass }), postcssRootFontSize, From 169d8aa1b6ed29852c509be7f6aaff9b31c10d7e Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Fri, 19 Nov 2021 22:28:07 +0900 Subject: [PATCH 3/6] Update tests --- src/markdown/inline_svg.js | 6 ++- src/marpit.js | 12 +++--- src/postcss/svg_backdrop.js | 2 +- src/theme_set.js | 10 +++-- test/markdown/background_image.js | 3 +- test/markdown/collect.js | 3 +- test/markdown/inline_svg.js | 7 +++- test/marpit.js | 70 +++++++++++++++++++++++++++---- 8 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/markdown/inline_svg.js b/src/markdown/inline_svg.js index 0d5d64a2..0626e001 100644 --- a/src/markdown/inline_svg.js +++ b/src/markdown/inline_svg.js @@ -16,7 +16,11 @@ function inlineSVG(md) { 'marpit_directives_parse', 'marpit_inline_svg', (state) => { - if (!marpit.inlineSVGOptions.enabled || state.inlineMode) return + if ( + !(marpit.inlineSVGOptions && marpit.inlineSVGOptions.enabled) || + state.inlineMode + ) + return const { themeSet, lastGlobalDirectives } = marpit const w = themeSet.getThemeProp(lastGlobalDirectives.theme, 'widthPixel') diff --git a/src/marpit.js b/src/marpit.js index fa062bbd..0062de8b 100644 --- a/src/marpit.js +++ b/src/marpit.js @@ -29,6 +29,11 @@ const defaultOptions = { inlineSVG: false, } +const defaultInlineSVGOptions = { + enabled: true, + backdropSelector: true, +} + /** * Parse Marpit Markdown and render to the slide HTML/CSS. */ @@ -244,13 +249,10 @@ class Marpit { */ get inlineSVGOptions() { if (typeof this.options.inlineSVG === 'object') { - return this.options.inlineSVG + return { ...defaultInlineSVGOptions, ...this.options.inlineSVG } } - return { - enabled: !!this.options.inlineSVG, - backdropSelector: true, - } + return { ...defaultInlineSVGOptions, enabled: !!this.options.inlineSVG } } /** diff --git a/src/postcss/svg_backdrop.js b/src/postcss/svg_backdrop.js index 4bd9bb31..9f3f86e6 100644 --- a/src/postcss/svg_backdrop.js +++ b/src/postcss/svg_backdrop.js @@ -32,7 +32,7 @@ const backdropMatcher = /(?:\b|^)::backdrop$/ * container whenever matched to `@media screen` media query. * * If concerned to conflict with the style provided by the app, consider to - * disable the selector support by `inlineSVG: { backdrop: false }`. + * disable the selector support by `inlineSVG: { backdropSelector: false }`. * * @see https://developer.mozilla.org/docs/Web/CSS/::backdrop * @alias module:postcss/svg_backdrop diff --git a/src/theme_set.js b/src/theme_set.js index 19acd368..62df1f1f 100644 --- a/src/theme_set.js +++ b/src/theme_set.js @@ -244,9 +244,11 @@ class ThemeSet { pack(name, opts = {}) { const slideElements = [{ tag: 'section' }] const theme = this.get(name, true) + const inlineSVGOpts = opts.inlineSVG || {} - if (opts.inlineSVG.enabled) + if (inlineSVGOpts.enabled) { slideElements.unshift({ tag: 'svg' }, { tag: 'foreignObject' }) + } const additionalCSS = (css) => { if (!css) return undefined @@ -284,8 +286,10 @@ class ThemeSet { 'marpit-pack-scaffold', () => (css) => css.first.before(scaffold.css) ), - opts.inlineSVG.enabled && postcssAdvancedBackground, - opts.inlineSVG.enabled && opts.inlineSVG.backdrop && postcssSVGBackdrop, + inlineSVGOpts.enabled && postcssAdvancedBackground, + inlineSVGOpts.enabled && + inlineSVGOpts.backdropSelector && + postcssSVGBackdrop, postcssPagination, postcssRootReplace({ pseudoClass }), postcssRootFontSize, diff --git a/test/markdown/background_image.js b/test/markdown/background_image.js index 17378682..2d3d96af 100644 --- a/test/markdown/background_image.js +++ b/test/markdown/background_image.js @@ -15,7 +15,8 @@ describe('Marpit background image plugin', () => { customDirectives: { global: {}, local: {} }, lastGlobalDirectives: {}, themeSet: { getThemeProp: () => 100 }, - options: { inlineSVG: svg }, + options: {}, + inlineSVGOptions: { enabled: svg }, }) const md = (svg = false) => { diff --git a/test/markdown/collect.js b/test/markdown/collect.js index 5bdbe280..73553fd1 100644 --- a/test/markdown/collect.js +++ b/test/markdown/collect.js @@ -15,7 +15,8 @@ describe('Marpit collect plugin', () => { themeSet, customDirectives: { global: {}, local: {} }, lastGlobalDirectives: {}, - options: { inlineSVG: svg }, + options: {}, + inlineSVGOptions: { enabled: svg }, }) const md = (marpitInstance) => { diff --git a/test/markdown/inline_svg.js b/test/markdown/inline_svg.js index 1cb43ca5..f7475f12 100644 --- a/test/markdown/inline_svg.js +++ b/test/markdown/inline_svg.js @@ -10,7 +10,8 @@ describe('Marpit inline SVG plugin', () => { customDirectives: { global: {}, local: {} }, themeSet: new ThemeSet(), lastGlobalDirectives: {}, - options: { inlineSVG: true }, + options: {}, + inlineSVGOptions: { enabled: true }, ...props, }) @@ -52,7 +53,9 @@ describe('Marpit inline SVG plugin', () => { }) it('ignores when Marpit inlineSVG option is false', () => { - const marpitStubInstance = marpitStub({ options: { inlineSVG: false } }) + const marpitStubInstance = marpitStub({ + inlineSVGOptions: { enabled: false }, + }) const $ = render(md(marpitStubInstance), '# test\n\n---\n\n# test') expect($('svg')).toHaveLength(0) }) diff --git a/test/marpit.js b/test/marpit.js index 30246cdc..38002954 100644 --- a/test/marpit.js +++ b/test/marpit.js @@ -175,7 +175,9 @@ describe('Marpit', () => { expect(opts.containers).toHaveLength(1) expect(opts.containers[0].tag).toBe('div') expect(opts.containers[0].class).toBe('marpit') - expect(opts.inlineSVG).toBe(false) + expect(opts.inlineSVG).toStrictEqual( + expect.objectContaining({ enabled: false }) + ) expect(opts.printable).toBe(true) return 'CSS' } @@ -233,6 +235,20 @@ describe('Marpit', () => { return declCount } + const backdropStyle = '' + const findBackdropRules = (css) => + postcssInstance.process(css, { from: undefined }).then((ret) => { + let rules = [] + + ret.root.walkDecls('--backdrop', (decl) => { + let node = decl + while (node.type !== 'rule' && node.parent) node = node.parent + if (node.type === 'rule') rules.push(node) + }) + + return rules + }) + it('has not svg when inlineSVG is false', () => { const rendered = instance(false).render('# Hi') const $ = cheerio.load(rendered.html, { lowerCaseTags: false }) @@ -240,6 +256,11 @@ describe('Marpit', () => { expect($('svg')).toHaveLength(0) }) + it('has not svg when inlineSVG option has enabled field as false', () => { + const rendered = instance({ enabled: false }).render('# Hi') + expect(rendered.html).not.toContain(' { const rendered = instance(true).render('# Hi') const $ = cheerio.load(rendered.html, { @@ -255,18 +276,51 @@ describe('Marpit', () => { }) }) + it('redirects ::backdrop selector to container SVG when inlineSVG is enabled', () => { + const { css } = instance(true).render(backdropStyle) + + return findBackdropRules(css).then((rules) => { + expect(rules).toHaveLength(2) + expect( + rules.find((r) => r.selector.endsWith('svg[data-marpit-svg]')) + ).toBeTruthy() + }) + }) + + it('wraps section with svg when inlineSVG is true', () => { + const rendered = instance({ enabled: true }).render('# Hi') + expect(rendered.html).toContain(' { it('outputs HTML including inline SVG as array', () => { - const { html } = instance(true).render('# Hi', { htmlAsArray: true }) - expect(html).toHaveLength(1) + for (const opt of [true, { enabled: true }]) { + const { html } = instance(opt).render('# Hi', { htmlAsArray: true }) + expect(html).toHaveLength(1) - const $ = cheerio.load(html[0], { - lowerCaseTags: false, - xmlMode: true, - }) - expect($('svg > foreignObject')).toHaveLength(1) + const $ = cheerio.load(html[0], { + lowerCaseTags: false, + xmlMode: true, + }) + expect($('svg > foreignObject')).toHaveLength(1) + } }) }) + + context( + 'when inlineSVG option has an object with backdropSelector field as false', + () => { + it('does not redirects ::backdrop selector to container SVG', () => { + const { css } = instance({ backdropSelector: false }).render( + backdropStyle + ) + + return findBackdropRules(css).then((rules) => { + expect(rules).toHaveLength(1) + }) + }) + } + ) }) describe('Background image', () => { From 29aa0976a0b8135392cd14d75ed129753a8f5e30 Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Fri, 19 Nov 2021 22:44:13 +0900 Subject: [PATCH 4/6] Update type definition --- index.d.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index a5b474ea..01ac6a70 100644 --- a/index.d.ts +++ b/index.d.ts @@ -13,11 +13,16 @@ declare namespace Marpit { markdown?: any printable?: boolean slideContainer?: false | Element | Element[] - inlineSVG?: boolean + inlineSVG?: boolean | InlineSVGOptions } type HeadingDivider = 1 | 2 | 3 | 4 | 5 | 6 + type InlineSVGOptions = { + enabled?: boolean + backdropSelector?: boolean + } + type RenderResult = { html: T css: string From d3a881e127119ebbc46454cf7f477074357145ec Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Sat, 20 Nov 2021 00:15:34 +0900 Subject: [PATCH 5/6] Add documentation of ::backdrop selector for inline SVG mode --- docs/inline-svg.md | 49 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/docs/inline-svg.md b/docs/inline-svg.md index 09eb2301..f2335443 100644 --- a/docs/inline-svg.md +++ b/docs/inline-svg.md @@ -17,9 +17,13 @@ When you set [`inlineSVG: true` in Marpit constructor option](/usage#triangular_ ``` +### Options + +`inlineSVG` constructor option also allows setting the option object. Refer to [Marpit API documentation](https://marpit-api.marp.app/marpit#~InlineSVGOptions) for details. + ## Motivation -It might feel a bit strange, but this approach has certain advantages. +You may feel it a bit strange. Why we have taken this approach? ### Pixel-perfect scaling @@ -57,9 +61,11 @@ Thus, a minimal web-based presentation no longer requires JavaScript. We strongl ### Isolated layer -Marpit's [advanced backgrounds](/image-syntax#advanced-backgrounds) would work within the isolated `` from the content. It means that the original Markdown DOM structure per page are keeping. +Marpit's [advanced backgrounds](/image-syntax#advanced-backgrounds) will work within the isolated `` from the content. It means that the original Markdown DOM structure per page are keeping. + +If advanced backgrounds were injected into the same layer as the Markdown content, inserted elements may break CSS selectors like [`:first-child` pseudo-class](https://developer.mozilla.org/docs/Web/CSS/:first-child) and [adjacent combinator (`+`)](https://developer.mozilla.org/docs/Web/CSS/Adjacent_sibling_combinator). -## Polyfill +## Webkit polyfill We provide a polyfill for WebKit based browsers in [@marp-team/marpit-svg-polyfill](https://github.com/marp-team/marpit-svg-polyfill). @@ -73,3 +79,40 @@ We provide a polyfill for WebKit based browsers in [@marp-team/marpit-svg-polyfi ``` + +## `::backdrop` CSS selector + +If enabled inline SVG mode, Marpit theme CSS and inline styles will redirect [`::backdrop` CSS selector](https://developer.mozilla.org/docs/Web/CSS/::backdrop) to the SVG container. + +A following rule matches to `` element. + +```css +::backdrop { + background-color: #448; +} +``` + +Some of Marpit integrated apps treats the background of SVG container as like as the backdrop of slide. By setting `background` style to the SVG container, you can change the color/image of slide [letterbox]()/[pillarbox](https://en.wikipedia.org/wiki/Pillarbox). + +Try resizing SVG container in below: + +
+ + +
+
+
+
+
+ +!> `::backdrop` pseudo-element does not limit applicable styles. To avoid unexpected effects into slides and apps, we strongly recommend to use this selector _only for changing the backdrop color_. + +### Disable + +If concerned conflict with styles for SVG container provided by app you are creating, you can disable `::backdrop` selector redirection separately by setting `backdropSelector` option as `false`. + +```javascript +const marpit = new Marpit({ + inlineSVG: { backdropSelector: false }, +}) +``` From 082b7dcb12a43504e06e52d2bd4e8572710db20f Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Sat, 20 Nov 2021 00:35:04 +0900 Subject: [PATCH 6/6] [ci skip] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bdaaf41..5979125e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +### Added + +- [`::backdrop` pseudo-element](https://marpit.marp.app/inline-svg?id=backdrop-css-selector) matches to the container SVG when enabled inline SVG mode ([#313](https://github.com/marp-team/marpit/issues/313), [#319](https://github.com/marp-team/marpit/pull/319)) +- Allow setting [the option object](https://marpit-api.marp.app/marpit#~InlineSVGOptions) to `inlineSVG` constructor option ([#319](https://github.com/marp-team/marpit/pull/319)) + ### Fixed - Remove recognized image keywords from alt text ([#316](https://github.com/marp-team/marpit/issues/316), [#318](https://github.com/marp-team/marpit/pull/318))