diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 774deebb3..88b4b5770 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -97,6 +97,7 @@ module.exports = {
collapsable: false,
children: [
['/guides/Replace-Rich-Text-Editor', 'Replace Rich Text Editor'],
+ ['/guides/Custom-CSS-parser', 'Use Custom CSS Parser'],
]
}
],
diff --git a/docs/.vuepress/public/cssom-devtools.png b/docs/.vuepress/public/cssom-devtools.png
new file mode 100644
index 000000000..6b4c68138
Binary files /dev/null and b/docs/.vuepress/public/cssom-devtools.png differ
diff --git a/docs/.vuepress/public/cssom-result.jpg b/docs/.vuepress/public/cssom-result.jpg
new file mode 100644
index 000000000..65e008828
Binary files /dev/null and b/docs/.vuepress/public/cssom-result.jpg differ
diff --git a/docs/guides/Custom-CSS-parser.md b/docs/guides/Custom-CSS-parser.md
new file mode 100644
index 000000000..026cd7f96
--- /dev/null
+++ b/docs/guides/Custom-CSS-parser.md
@@ -0,0 +1,242 @@
+---
+title: Use Custom CSS Parser
+---
+# Use Custom CSS Parser
+
+If you just use GrapesJS for building templates from scratch, so you start from an empty canvas and for editing you strictly rely on the generated JSON (final HTML/CSS only for end-users) then, probably, you might skip this guide. On the other hand, if you import templates from already defined HTML/CSS or let the user embed custom codes (eg. using the [grapesjs-custom-code](https://github.com/artf/grapesjs-custom-code) plugin), then you have to know that you might face strange behaviors.
+
+[[toc]]
+
+
+## Import HTML/CSS
+
+Importing already defined HTML/CSS is a really good feature as it lets you start editing immediately any kind of template and obviously, GrapesJS itself promotes this kind of approach
+
+```html
+
+
+
+```
+
+To work fast and easier GrapesJS needs to compile a simple string (HTML/CSS) into structured nodes (nested JS objects). Fortunately, most of the hard work (parsing) is already done by the browser itself which translates that string into its own objects ([DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)/[CSSOM](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model)) and so we just rely on those, by traversing them and creating our nodes (unfortunately browser's objects are not enough). The fact we're able to parse our strings just by using the browser itself it's very cool, we can enable the import feature without requiring any third-party library, so... where is the problem? Well, while the generated DOM is performing quite well, as we're able to extract what we need, unfortunately, it's not the same for the CSSOM, so let's see in the next paragraph what is wrong with it.
+
+## CSSOM results are inconsistent
+
+Unfortunately, we have discovered that the CSSOM generated by browsers are highly inconsistent from what we ask to parse. To demonstrate it, we gonna create a simple example by using the built-in parser and we'll check its result.
+So, for our case we just take in account a simple rule, we'll parse it and print the CSSOM result on screen.
+
+```html
+
+To parse
+
+ .simple-class {
+ background-image:url("https://image1.png"), url("https://image2.jpg");
+ background-attachment: fixed, scroll;
+ background-position:left top, center center;
+ background-repeat:repeat-y, no-repeat;
+ background-size: contain, cover;
+ box-shadow: 0 0 5px #9d7aa5, 0 0 10px #e6c3ee;
+ border: 2px solid #FF0000;
+ }
+
+
+Result
+
+
+
+```
+
+### Results
+Here some results (using latest versions + IE11)
+
+
+
+As you see, this is what we get for asking only 7 properties, who adds more or less, someone converts colors to rgba functions and someone else changes the order of our values (eg. `box-shadow`). Webkit-based browsers attach also properties they self don't understand
+
+
+
+So it's clear that we can't rely on CSSOM objects, that's why we added the possibility to set custom CSS parser via `editor.setCustomParserCss` method or `config.Parser.parserCss` option to use on initialization. Let's see in detail how it's expected to work
+
+## Set CSS parser
+
+The custom parser you have to use it's just a function receiving 2 arguments: `css`, as the CSS string to parse, and `editor`, the instance of the current editor. As the result, you should return an array containing valid rule objects, the syntax of those objects are explained below. This is how you can set the custom parser
+
+```js
+const parserCss = (css, editor) => {
+ const result = [];
+ // ... parse the CSS string
+ result.push({
+ selectors: '.someclass, div .otherclass',
+ style: { color: 'red' }
+ })
+ // ...
+ return result; // Result should be ALWAYS an array
+};
+
+// On initialization
+// This is the recommended way, as you gonna use the parser from the beginning
+const editor = grapesjs.init({
+ //...
+ parser: {
+ parserCss,
+ }
+});
+
+// Or later, via editor API
+editor.setCustomParserCss(parserCss);
+```
+
+## Rule Objects
+
+The syntax of rule objects is pretty straightforward, each object might contain following keys
+
+| Key | Description | Example |
+|-|-|-
+| `selectors` | Selectors of the rule.
**REQUIRED** return an empty string in case the rule has no selectors | `.class1, div > #someid` |
+| `style` | Style declarations as an object | `{ color: 'red' }` |
+| `atRule` | At-rule name | `media` |
+| `params` | Parameters of the at-rule | `screen and (min-width: 480px)` |
+
+To make it more clear let's see a few examples
+
+```js
+// Input
+`
+@font-face {
+ font-family: "Font Name";
+ src: url("https://font-url.eot");
+}
+`
+// Output
+[
+ {
+ selectors: '',
+ atRule: 'font-face',
+ style: {
+ 'font-family': '"Font Name"',
+ src: 'url("https://font-url.eot")',
+ },
+ }
+]
+
+// Input
+`
+@keyframes keyframe-name {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+`
+// Output
+[
+ {
+ params: 'keyframe-name',
+ selectors: 'from',
+ atRule: 'keyframes',
+ style: {
+ opacity: '0',
+ },
+ }, {
+ params: 'keyframe-name',
+ selectors: 'to',
+ atRule: 'keyframes',
+ style: {
+ opacity: '1',
+ },
+ }
+]
+
+// Input
+`
+@media screen and (min-width: 480px) {
+ body {
+ background-color: lightgreen;
+ }
+
+ .class-test, .class-test2:hover {
+ color: blue !important;
+ }
+}
+`
+// Output
+[
+ {
+ params: 'screen and (min-width: 480px)',
+ selectors: 'body',
+ atRule: 'media',
+ style: {
+ 'background-color': 'lightgreen',
+ },
+ }, {
+ params: 'screen and (min-width: 480px)',
+ selectors: '.class-test, .class-test2:hover',
+ atRule: 'media',
+ style: {
+ color: 'blue !important',
+ },
+ }
+]
+
+// Input
+`
+:root {
+ --some-color: red;
+ --some-width: 55px;
+}
+`
+// Output
+[
+ {
+ selectors: ':root',
+ style: {
+ '--some-color': 'red',
+ '--some-width': '55px',
+ },
+ },
+]
+```
+
+## Plugins
+
+Below the list of current available CSS parsers as plugins, if you need to create your own we highly suggest to explore their sources
+
+* [grapesjs-parser-postcss](https://github.com/artf/grapesjs-parser-postcss) - Using [PostCSS](https://github.com/postcss/postcss) parser