diff --git a/docs/assets/img/builds-content-styles.png b/docs/assets/img/builds-content-styles.png
new file mode 100644
index 00000000000..e661785e395
Binary files /dev/null and b/docs/assets/img/builds-content-styles.png differ
diff --git a/docs/builds/guides/faq.md b/docs/builds/guides/faq.md
index e883dc72d64..d36f1672534 100644
--- a/docs/builds/guides/faq.md
+++ b/docs/builds/guides/faq.md
@@ -22,15 +22,9 @@ See the [relevant issue](https://github.com/ckeditor/ckeditor5/issues/592) on Gi
## What happened to the `contents.css` file? How do I style the content of the editor?
-There is no such thing as the `contents.css` file because in CKEditor 5 features bring their own content styles, which are by default included in the JavaScript build and {@link framework/guides/theme-customization#styles-processing-and-bundling loaded by the style–loader} (they can be {@link builds/guides/integration/advanced-setup#option-extracting-css extracted}, too). It optimizes the size of the builds as the styles of unused features are simply excluded.
+There is no such thing as the `contents.css` file because in CKEditor 5 features bring their own content styles, which are by default included in the JavaScript build and {@link framework/guides/theme-customization#styles-processing-and-bundling loaded by the style–loader}. It optimizes the size of the builds as the styles of unused features are simply excluded.
-Although some styles are provided by the features, it is up to the developers to make sure the content created by CKEditor 5 is properly styled, both in the front–end and in the back–end. To style the content in the editor (back–end), use the `.ck-content` CSS class:
-
-```css
-.ck-content a {
- color: teal;
-}
-```
+You can get the full list of editor content styles in a {@link builds/guides/integration/content-styles dedicated guide}. You can also {@link builds/guides/integration/advanced-setup#option-extracting-css extract} all CSS brought by CKEditor 5 (content and UI) to a separate file when creating a custom editor build.
## The build I downloaded is missing some features. How do I add them?
diff --git a/docs/builds/guides/integration/content-styles.md b/docs/builds/guides/integration/content-styles.md
new file mode 100644
index 00000000000..486447de22b
--- /dev/null
+++ b/docs/builds/guides/integration/content-styles.md
@@ -0,0 +1,194 @@
+---
+# Scope:
+# * Explain what content styles are and how to use them.
+# * Offer developers a way to obtain editor content styles.
+
+category: builds-integration
+order: 80
+---
+
+# Content styles
+
+Some of the {@link features/index core editor features} bring additional CSS to control the look of the content they produce. Take, for example, the {@link features/image Image feature} that needs special content styles to render images and their captions in the content. Or the {@link module:block-quote/blockquote~BlockQuote Block quote} plugin displays quotes in italic with a subtle border on the side.
+
+{@img assets/img/builds-content-styles.png 823 Editor content styles.}
+
+Content styles are bundled along with editor UI styles and, together with the JavaScript code of CKEditor 5, they create a monolithic structure called an {@link builds/guides/overview#available-builds editor build}. By default, content styles are inseparable from the rest of the editor which means there is no CSS file containing them you could take straight from the editor and use in your application (as opposed to the CKEditor 4 `contents.css` file). To get editor content styles, for instance, for the front–end of your application, you will have to take additional steps described in this guide.
+
+## Sharing content styles between front–end and back–end
+
+By default, content styles are loaded by the editor JavaScript which makes them only present when users edit their content and this, in turn, usually takes place in the back–end of an application. You want to use the same styles in the front–end, you may find yourself in a situation that requires you to load CKEditor just for that purpose, which is (performance–wise) not the best idea.
+
+To avoid unnecessary dependencies in your front–end use a stylesheet with a complete list of CKEditor 5 content styles used by all editor features. There are two ways to obtain it:
+
+* By taking it directly from [this guide](#the-full-list-of-content-styles) and saving it as a static resource in your application (e.g. `content-styles.css`) (**recommended**).
+* By generating it using a dedicated script. Learn more in the {@link framework/guides/contributing/development-environment#generating-content-styles "Development environment"} guide.
+
+Load the `content-styles.css` file in your application by adding the following code to the template:
+
+```html
+
+```
+
+The content in the front–end of your application should now look the same as when edited by the users.
+
+
+ **Important!**
+
+ If you take a closer look at the content styles you may notice they all are prefixed by the `.ck-content` class selector. This narrows their scope when used in CKEditor so they do not affect the rest of the application. To use them in the front–end, **you will have to** add the `ck-content` CSS class to the container of your content. Otherwise styles will not apply.
+
+
+
+ If you are not afraid to get your hands dirty, you can always create a custom CKEditor 5 build from the source code with **all** the CSS (both UI and the content) extracted to a separate file. See how to do that in a {@link builds/guides/integration/advanced-setup#option-extracting-css dedicated guide}.
+
+
+## The full list of content styles
+
+Below there is a full list of content styles used by editor features. You can copy it and use straight in your project. **Make sure to add the `ck-content` class to your content container for the styles to work** ([see above](#sharing-content-styles-between-frontend-and-backend)).
+
+```css
+/*
+ * CKEditor 5 (v12.3.1) content styles.
+ * Generated on Tue, 06 Aug 2019 09:44:26 GMT.
+ * For more information, check out https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html
+ */
+
+:root {
+ --ck-image-style-spacing: 1.5em;
+}
+
+/* ckeditor5-image/theme/imagecaption.css */
+.ck-content .image > figcaption {
+ display: table-caption;
+ caption-side: bottom;
+ word-break: break-word;
+ color: hsl(0, 0%, 20%);
+ background-color: hsl(0, 0%, 97%);
+ padding: .6em;
+ font-size: .75em;
+ outline-offset: -1px;
+}
+/* ckeditor5-basic-styles/theme/code.css */
+.ck-content code {
+ background-color: hsla(0, 0%, 78%, 0.3);
+ padding: .15em;
+ border-radius: 2px;
+}
+/* ckeditor5-table/theme/table.css */
+.ck-content .table {
+ margin: 1em auto;
+ display: table
+}
+/* ckeditor5-table/theme/table.css */
+.ck-content .table table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ border: 1px double hsl(0, 0%, 70%)
+}
+/* ckeditor5-table/theme/table.css */
+.ck-content .table table td,
+.ck-content .table table th {
+ min-width: 2em;
+ padding: .4em;
+ border-color: hsl(0, 0%, 85%);
+}
+/* ckeditor5-table/theme/table.css */
+.ck-content .table table td,
+.ck-content .table table th {
+ min-width: 2em;
+ padding: .4em;
+ border-color: hsl(0, 0%, 85%);
+}
+/* ckeditor5-table/theme/table.css */
+.ck-content .table table th {
+ font-weight: bold;
+ background: hsl(0, 0%, 98%);
+}
+/* ckeditor5-image/theme/image.css */
+.ck-content .image {
+ display: table;
+ clear: both;
+ text-align: center;
+ margin: 1em auto
+}
+/* ckeditor5-image/theme/image.css */
+.ck-content .image > img {
+ display: block;
+ margin: 0 auto;
+ max-width: 100%;
+ min-width: 50px;
+}
+/* ckeditor5-image/theme/imageuploadprogress.css */
+.ck-content .image {
+ position: relative;
+ overflow: hidden;
+}
+/* ckeditor5-image/theme/imageuploadprogress.css */
+.ck-content .image.ck-appear {
+ animation: fadeIn 700ms;
+}
+/* ckeditor5-image/theme/imagestyle.css */
+.ck-content .image-style-side,
+.ck-content .image-style-align-left,
+.ck-content .image-style-align-center,
+.ck-content .image-style-align-right {
+ max-width: 50%;
+}
+/* ckeditor5-image/theme/imagestyle.css */
+.ck-content .image-style-side,
+.ck-content .image-style-align-left,
+.ck-content .image-style-align-center,
+.ck-content .image-style-align-right {
+ max-width: 50%;
+}
+/* ckeditor5-image/theme/imagestyle.css */
+.ck-content .image-style-side,
+.ck-content .image-style-align-left,
+.ck-content .image-style-align-center,
+.ck-content .image-style-align-right {
+ max-width: 50%;
+}
+/* ckeditor5-image/theme/imagestyle.css */
+.ck-content .image-style-side,
+.ck-content .image-style-align-left,
+.ck-content .image-style-align-center,
+.ck-content .image-style-align-right {
+ max-width: 50%;
+}
+/* ckeditor5-image/theme/imagestyle.css */
+.ck-content .image-style-side {
+ float: right;
+ margin-left: var(--ck-image-style-spacing);
+}
+/* ckeditor5-image/theme/imagestyle.css */
+.ck-content .image-style-align-left {
+ float: left;
+ margin-right: var(--ck-image-style-spacing);
+}
+/* ckeditor5-image/theme/imagestyle.css */
+.ck-content .image-style-align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+/* ckeditor5-image/theme/imagestyle.css */
+.ck-content .image-style-align-right {
+ float: right;
+ margin-left: var(--ck-image-style-spacing);
+}
+/* ckeditor5-block-quote/theme/blockquote.css */
+.ck-content blockquote {
+ overflow: hidden;
+ padding-right: 1.5em;
+ padding-left: 1.5em;
+ margin-left: 0;
+ font-style: italic;
+ border-left: solid 5px hsl(0, 0%, 80%);
+}
+/* ckeditor5-media-embed/theme/mediaembed.css */
+.ck-content .media {
+ clear: both;
+ margin: 1em 0;
+ display: block;
+ min-width: 15em;
+}
+```
diff --git a/docs/framework/guides/contributing/development-environment.md b/docs/framework/guides/contributing/development-environment.md
index 1c48f68d542..7cc243192e0 100644
--- a/docs/framework/guides/contributing/development-environment.md
+++ b/docs/framework/guides/contributing/development-environment.md
@@ -223,6 +223,10 @@ Note: These arguments must be passed after additional `--`:
yarn run docs --skip-api
```
+## Generating content styles
+
+It is possible to generate a stylesheet containing content styles brought by all CKEditor 5 features. Execute `yarn docs:content-styles` and the stylesheet will be saved in the `build/content-styles` folder. To learn more, please refer to the {@link builds/guides/integration/content-styles "Content styles"} guide.
+
## Bisecting through a multi-repository
CKEditor 5 is a multi-repository project. It means that [`git bisect`](https://git-scm.com/docs/git-bisect) (which is super handy when tracking which commit introduced a bug) will not work out of the box.
diff --git a/package.json b/package.json
index e7210ebd48e..4e8c74c69f9 100644
--- a/package.json
+++ b/package.json
@@ -120,6 +120,7 @@
"docs:api": "node ./scripts/docs/build-api-docs.js",
"docs:build-and-publish": "node ./scripts/docs/build-and-publish.js",
"docs:build-and-publish-nightly": "node ./scripts/docs/build-and-publish-nightly.js",
+ "docs:content-styles": "node ./scripts/docs/build-content-styles.js",
"translations:collect": "ckeditor5-dev-env-translations collect",
"translations:download": "ckeditor5-dev-env-translations download",
"translations:upload": "ckeditor5-dev-env-translations upload",
diff --git a/scripts/docs/build-content-styles.js b/scripts/docs/build-content-styles.js
new file mode 100644
index 00000000000..a62b99233ea
--- /dev/null
+++ b/scripts/docs/build-content-styles.js
@@ -0,0 +1,241 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* eslint-env node */
+
+const path = require( 'path' );
+const fs = require( 'fs' );
+const webpack = require( 'webpack' );
+const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
+const { version } = require( '../../package.json' );
+
+const DESTINATION_DIRECTORY = path.join( __dirname, '..', '..', 'build', 'content-styles' );
+const DOCUMENTATION_URL = 'https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html';
+const VARIABLE_DEFINITION_REGEXP = /(--[\w-]+):\s+(.*);/g;
+const VARIABLE_USAGE_REGEXP = /var\((--[\w-]+)\)/g;
+
+const contentRules = {
+ selector: [],
+ variables: []
+};
+
+const webpackConfig = getWebpackConfig();
+const packagesPath = path.join( process.cwd(), 'packages' );
+
+runWebpack( webpackConfig )
+ .then( () => {
+ // All variables are placed inside `:root` selector. Let's extract their names and values as a map.
+ const cssVariables = new Map( contentRules.variables
+ .map( rule => {
+ // Let's extract all of them as an array of pairs: [ name, value ]
+ const allRules = [];
+ let match;
+
+ while ( ( match = VARIABLE_DEFINITION_REGEXP.exec( rule.css ) ) ) {
+ allRules.push( [ match[ 1 ], match[ 2 ] ] );
+ }
+
+ return allRules;
+ } )
+ .reduce( ( previousValue, currentValue ) => {
+ // And simplify nested arrays as a flattened array.
+ previousValue.push( ...currentValue );
+
+ return previousValue;
+ }, [] ) );
+
+ // CSS variables that are used by the `.ck-content` selector.
+ const usedVariables = new Set();
+
+ const selectorCss = contentRules.selector
+ .map( rule => {
+ // Removes all comments from the rule definition.
+ const cssAsArray = rule.css.replace( /\/\*[^*]+\*\//g, '' ).split( '\n' );
+
+ // We want to fix invalid indentations. We need to find a number of how many indentations we want to remove.
+ // Because the last line ends the block, we can use this value.
+ const lastLineIndent = cssAsArray[ cssAsArray.length - 1 ].length - 1;
+
+ const css = cssAsArray
+ .filter( line => line.trim().length > 0 )
+ .map( ( line, index ) => {
+ // Do not touch the first line. It is always correct.
+ if ( index === 0 ) {
+ return line;
+ }
+
+ return line.slice( lastLineIndent );
+ } )
+ .join( '\n' );
+
+ return `/* ${ rule.file.replace( packagesPath + path.sep, '' ) } */\n${ css }`;
+ } )
+ .filter( rule => {
+ // 1st: path to the css file, 2nd: selector definition - start block, 3rd: end block
+ // If the rule contains only 3 lines, it means that it does not define any rules.
+ return rule.split( '\n' ).length > 3;
+ } )
+ .join( '\n' );
+
+ // Find all CSS variables inside `.ck-content` selector.
+ let match;
+
+ while ( ( match = VARIABLE_USAGE_REGEXP.exec( selectorCss ) ) ) {
+ usedVariables.add( match[ 1 ] );
+ }
+
+ // We need to also look at whether any of the used variables requires the value of other variables.
+ let clearRun = false;
+
+ // We need to process all variables as long as the entire collection won't be changed.
+ while ( !clearRun ) {
+ clearRun = true;
+
+ // For the every used variable...
+ for ( const variable of usedVariables ) {
+ const value = cssVariables.get( variable );
+
+ let match;
+
+ // ...find its value and check whether it requires another variable.
+ while ( ( match = VARIABLE_USAGE_REGEXP.exec( value ) ) ) {
+ // If so, mark the entire `while()` block as it should be checked once again.
+ // Also, add the new variable to used variables collection.
+ if ( !usedVariables.has( match[ 1 ] ) ) {
+ clearRun = false;
+ usedVariables.add( match[ 1 ] );
+ }
+ }
+ }
+ }
+
+ // Build the final content of the CSS file.
+ let data = [
+ '/*',
+ ` * CKEditor 5 (v${ version }) content styles.`,
+ ` * Generated on ${ new Date().toUTCString() }.`,
+ ` * For more information, check out ${ DOCUMENTATION_URL }`,
+ ' */\n\n',
+ ].join( '\n' );
+
+ data += ':root {\n';
+
+ for ( const variable of [ ...usedVariables ].sort() ) {
+ data += `\t${ variable }: ${ cssVariables.get( variable ) };\n`;
+ }
+
+ data += '}\n\n';
+ data += selectorCss;
+
+ return writeFile( path.join( DESTINATION_DIRECTORY, 'content-styles.css' ), data );
+ } )
+ .then( () => {
+ console.log( `Content styles have been extracted to ${ path.join( DESTINATION_DIRECTORY, 'content-styles.css' ) }` );
+ } )
+ .catch( err => {
+ console.log( err );
+ } );
+
+/**
+ * Prepares configuration for Webpack.
+ *
+ * @returns {Object}
+ */
+function getWebpackConfig() {
+ const postCssConfig = styles.getPostCssConfig( {
+ themeImporter: {
+ themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
+ },
+ minify: false
+ } );
+
+ const contentStylesPlugin = require( './content-styles/list-content-styles' )( { contentRules } );
+
+ postCssConfig.plugins.push( contentStylesPlugin );
+
+ return {
+ mode: 'development',
+
+ devtool: 'source-map',
+
+ entry: {
+ ckeditor5: path.join( __dirname, 'content-styles', 'ckeditor.js' )
+ },
+
+ output: {
+ path: DESTINATION_DIRECTORY,
+ filename: '[name].js'
+ },
+
+ // Configure the paths so building CKEditor 5 snippets work even if the script
+ // is triggered from a directory outside ckeditor5 (e.g. multi-project case).
+ resolve: {
+ modules: getModuleResolvePaths()
+ },
+
+ resolveLoader: {
+ modules: getModuleResolvePaths()
+ },
+
+ module: {
+ rules: [
+ {
+ test: /\.svg$/,
+ use: [ 'raw-loader' ]
+ },
+ {
+ test: /\.css$/,
+ use: [
+ 'style-loader',
+ {
+ loader: 'postcss-loader',
+ options: postCssConfig
+ }
+ ]
+ }
+ ]
+ }
+ };
+}
+
+/**
+ * @param {Object} webpackConfig
+ * @returns {Promise}
+ */
+function runWebpack( webpackConfig ) {
+ return new Promise( ( resolve, reject ) => {
+ webpack( webpackConfig, ( err, stats ) => {
+ if ( err ) {
+ reject( err );
+ } else if ( stats.hasErrors() ) {
+ reject( new Error( stats.toString() ) );
+ } else {
+ resolve();
+ }
+ } );
+ } );
+}
+
+/**
+ * @returns {Array.}
+ */
+function getModuleResolvePaths() {
+ return [
+ path.resolve( __dirname, '..', '..', 'node_modules' ),
+ 'node_modules'
+ ];
+}
+
+function writeFile( file, data ) {
+ return new Promise( ( resolve, reject ) => {
+ fs.writeFile( file, data, err => {
+ if ( err ) {
+ return reject( err );
+ }
+
+ return resolve();
+ } );
+ } );
+}
diff --git a/scripts/docs/content-styles/ckeditor.js b/scripts/docs/content-styles/ckeditor.js
new file mode 100644
index 00000000000..f952f0d780a
--- /dev/null
+++ b/scripts/docs/content-styles/ckeditor.js
@@ -0,0 +1,72 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+// The editor creator to use.
+import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+
+import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
+import UploadAdapter from '@ckeditor/ckeditor5-adapter-ckfinder/src/uploadadapter';
+import Autoformat from '@ckeditor/ckeditor5-autoformat/src/autoformat';
+import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+import Code from '@ckeditor/ckeditor5-basic-styles/src/code';
+import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+import Strikethrough from '@ckeditor/ckeditor5-basic-styles/src/strikethrough';
+import Subscript from '@ckeditor/ckeditor5-basic-styles/src/subscript';
+import Superscript from '@ckeditor/ckeditor5-basic-styles/src/superscript';
+import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
+import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote';
+import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder';
+import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
+import Heading from '@ckeditor/ckeditor5-heading/src/heading';
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
+import ImageStyle from '@ckeditor/ckeditor5-image/src/imagestyle';
+import ImageToolbar from '@ckeditor/ckeditor5-image/src/imagetoolbar';
+import ImageUpload from '@ckeditor/ckeditor5-image/src/imageupload';
+import Link from '@ckeditor/ckeditor5-link/src/link';
+import List from '@ckeditor/ckeditor5-list/src/list';
+import MediaEmbed from '@ckeditor/ckeditor5-media-embed/src/mediaembed';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import PasteFromOffice from '@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice';
+import Table from '@ckeditor/ckeditor5-table/src/table';
+import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
+import Font from '@ckeditor/ckeditor5-font/src/font';
+import Highlight from '@ckeditor/ckeditor5-highlight/src/highlight';
+import Alignment from '@ckeditor/ckeditor5-alignment/src/alignment';
+
+export default class ClassicEditor extends ClassicEditorBase {}
+
+// Plugins to include in the build.
+ClassicEditor.builtinPlugins = [
+ Essentials,
+ UploadAdapter,
+ Autoformat,
+ Bold,
+ Code,
+ Italic,
+ Strikethrough,
+ Subscript,
+ Superscript,
+ Underline,
+ BlockQuote,
+ CKFinder,
+ EasyImage,
+ Heading,
+ Image,
+ ImageCaption,
+ ImageStyle,
+ ImageToolbar,
+ ImageUpload,
+ Link,
+ List,
+ MediaEmbed,
+ Paragraph,
+ PasteFromOffice,
+ Table,
+ TableToolbar,
+ Font,
+ Highlight,
+ Alignment
+];
diff --git a/scripts/docs/content-styles/list-content-styles.js b/scripts/docs/content-styles/list-content-styles.js
new file mode 100644
index 00000000000..22794664532
--- /dev/null
+++ b/scripts/docs/content-styles/list-content-styles.js
@@ -0,0 +1,35 @@
+#!/usr/bin/env node
+
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* eslint-env node */
+
+const postcss = require( 'postcss' );
+
+module.exports = postcss.plugin( 'list-content-styles', function( options ) {
+ const selectorStyles = options.contentRules.selector;
+ const variables = options.contentRules.variables;
+
+ return root => {
+ root.walkRules( rule => {
+ rule.selectors.forEach( selector => {
+ if ( selector.match( ':root' ) ) {
+ variables.push( {
+ file: root.source.input.file,
+ css: rule.toString()
+ } );
+ }
+
+ if ( selector.match( '.ck-content' ) ) {
+ selectorStyles.push( {
+ file: root.source.input.file,
+ css: rule.toString()
+ } );
+ }
+ } );
+ } );
+ };
+} );