diff --git a/markwon-test-util/build.gradle b/markwon-test-util/build.gradle
deleted file mode 100644
index 3909ab27..00000000
--- a/markwon-test-util/build.gradle
+++ /dev/null
@@ -1,13 +0,0 @@
-apply plugin: 'java-library'
-
-sourceCompatibility = 1.7
-targetCompatibility = 1.7
-
-dependencies {
-
-    api deps['support-annotations']
-
-    deps['test'].with {
-        implementation it['commons-io']
-    }
-}
\ No newline at end of file
diff --git a/markwon-test-util/src/main/java/ru/noties/markwon/test/TestUtil.java b/markwon-test-util/src/main/java/ru/noties/markwon/test/TestUtil.java
deleted file mode 100644
index 2ce00e1f..00000000
--- a/markwon-test-util/src/main/java/ru/noties/markwon/test/TestUtil.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package ru.noties.markwon.test;
-
-import android.support.annotation.NonNull;
-
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-public abstract class TestUtil {
-
-    @NonNull
-    public static String read(@NonNull String path) {
-        try {
-            return IOUtils.resourceToString(path, StandardCharsets.UTF_8);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @NonNull
-    public static String read(@NonNull Object who, @NonNull String path) {
-        try {
-            return IOUtils.resourceToString(path, StandardCharsets.UTF_8, who.getClass().getClassLoader());
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private TestUtil() {
-    }
-}
diff --git a/markwon/build.gradle b/markwon/build.gradle
index 604519a2..8682d261 100644
--- a/markwon/build.gradle
+++ b/markwon/build.gradle
@@ -23,17 +23,12 @@ dependencies {
     deps['test'].with {
 
         testImplementation project(':markwon-test-span')
-        testImplementation project(':markwon-test-util')
 
         testImplementation it['junit']
         testImplementation it['robolectric']
         testImplementation it['mockito']
 
-        // to remove after migration
         testImplementation it['ix-java']
-        testImplementation it['jackson-yaml']
-        testImplementation it['jackson-databind']
-        testImplementation it['gson']
         testImplementation it['commons-io']
     }
 }
diff --git a/markwon/src/main/java/ru/noties/markwon/AbstractMarkwonPlugin.java b/markwon/src/main/java/ru/noties/markwon/AbstractMarkwonPlugin.java
index 28767521..d9849e5c 100644
--- a/markwon/src/main/java/ru/noties/markwon/AbstractMarkwonPlugin.java
+++ b/markwon/src/main/java/ru/noties/markwon/AbstractMarkwonPlugin.java
@@ -1,6 +1,7 @@
 package ru.noties.markwon;
 
 import android.support.annotation.NonNull;
+import android.text.Spanned;
 import android.widget.TextView;
 
 import org.commonmark.node.Node;
@@ -9,63 +10,107 @@ import org.commonmark.parser.Parser;
 import ru.noties.markwon.core.MarkwonTheme;
 import ru.noties.markwon.image.AsyncDrawableLoader;
 
+/**
+ * Class that extends {@link MarkwonPlugin} with all methods implemented (empty body)
+ * for easier plugin implementation. Only required methods can be overriden
+ *
+ * @see MarkwonPlugin
+ * @since 3.0.0
+ */
 public abstract class AbstractMarkwonPlugin implements MarkwonPlugin {
+
+    /**
+     * @inheritDoc
+     */
     @Override
     public void configureParser(@NonNull Parser.Builder builder) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void configureTheme(@NonNull MarkwonTheme.Builder builder) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void configureRenderProps(@NonNull RenderProps renderProps) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @NonNull
     @Override
     public String processMarkdown(@NonNull String markdown) {
         return markdown;
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void beforeRender(@NonNull Node node) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
-    public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) {
+    public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
 
     }
 
+    /**
+     * @inheritDoc
+     */
     @Override
     public void afterSetText(@NonNull TextView textView) {
 
diff --git a/markwon/src/main/java/ru/noties/markwon/Markwon.java b/markwon/src/main/java/ru/noties/markwon/Markwon.java
index 6c4e4bea..c5a9ea8c 100644
--- a/markwon/src/main/java/ru/noties/markwon/Markwon.java
+++ b/markwon/src/main/java/ru/noties/markwon/Markwon.java
@@ -7,9 +7,20 @@ import android.widget.TextView;
 
 import org.commonmark.node.Node;
 
+/**
+ * Class to parse and render markdown. Since version 3.0.0 instance specific (previously consisted
+ * of static stateless methods). An instance of builder can be obtained via {@link #builder(Context)}
+ * method.
+ *
+ * @see #builder(Context)
+ * @see Builder
+ */
 public abstract class Markwon {
 
     /**
+     * Factory method to obtain an instance of {@link Builder}
+     *
+     * @see Builder
      * @since 3.0.0
      */
     @NonNull
@@ -17,6 +28,14 @@ public abstract class Markwon {
         return new MarkwonBuilderImpl(context);
     }
 
+    /**
+     * Method to simply parse markdown (without rendering)
+     *
+     * @param input markdown input to parse
+     * @return parsed via commonmark-java <code>org.commonmark.node.Node</code>
+     * @see #render(Node)
+     * @since 3.0.0
+     */
     @NonNull
     public abstract Node parse(@NonNull String input);
 
@@ -29,9 +48,14 @@ public abstract class Markwon {
 
     public abstract void setMarkdown(@NonNull TextView textView, @NonNull String markdown);
 
-    public abstract void setParsedMarkdown(@NonNull TextView textView, @NonNull CharSequence markdown);
+    public abstract void setParsedMarkdown(@NonNull TextView textView, @NonNull Spanned markdown);
 
     /**
+     * Builder for {@link Markwon}.
+     * <p>
+     * Please note that the order in which plugins are supplied is important as this order will be
+     * used through the whole usage of built Markwon instance
+     *
      * @since 3.0.0
      */
     public interface Builder {
diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonBuilderImpl.java b/markwon/src/main/java/ru/noties/markwon/MarkwonBuilderImpl.java
index 6098155a..1cf15c2c 100644
--- a/markwon/src/main/java/ru/noties/markwon/MarkwonBuilderImpl.java
+++ b/markwon/src/main/java/ru/noties/markwon/MarkwonBuilderImpl.java
@@ -80,7 +80,6 @@ class MarkwonBuilderImpl implements Markwon.Builder {
             plugin.configureConfiguration(configurationBuilder);
             plugin.configureVisitor(visitorBuilder);
             plugin.configureSpansFactory(spanFactoryBuilder);
-            plugin.configureRenderProps(renderProps);
         }
 
         final MarkwonConfiguration configuration = configurationBuilder.build(
diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonImpl.java b/markwon/src/main/java/ru/noties/markwon/MarkwonImpl.java
index 2114f050..8e2d8791 100644
--- a/markwon/src/main/java/ru/noties/markwon/MarkwonImpl.java
+++ b/markwon/src/main/java/ru/noties/markwon/MarkwonImpl.java
@@ -9,6 +9,9 @@ import org.commonmark.parser.Parser;
 
 import java.util.List;
 
+/**
+ * @since 3.0.0
+ */
 class MarkwonImpl extends Markwon {
 
     private final TextView.BufferType bufferType;
@@ -31,6 +34,7 @@ class MarkwonImpl extends Markwon {
     @Override
     public Node parse(@NonNull String input) {
 
+        // make sure that all plugins are called `processMarkdown` before parsing
         for (MarkwonPlugin plugin : plugins) {
             input = plugin.processMarkdown(input);
         }
@@ -42,7 +46,14 @@ class MarkwonImpl extends Markwon {
     @Override
     public Spanned render(@NonNull Node node) {
 
+        final RenderProps renderProps = visitor.renderProps();
+
         for (MarkwonPlugin plugin : plugins) {
+
+            // let plugins apply render properties before rendering (as we will clear
+            // renderProps after rendering)
+            plugin.configureRenderProps(renderProps);
+
             plugin.beforeRender(node);
         }
 
@@ -52,6 +63,9 @@ class MarkwonImpl extends Markwon {
             plugin.afterRender(node, visitor);
         }
 
+        // clear render props after rending
+        renderProps.clearAll();
+
         return visitor.builder().spannableStringBuilder();
     }
 
@@ -67,7 +81,7 @@ class MarkwonImpl extends Markwon {
     }
 
     @Override
-    public void setParsedMarkdown(@NonNull TextView textView, @NonNull CharSequence markdown) {
+    public void setParsedMarkdown(@NonNull TextView textView, @NonNull Spanned markdown) {
 
         for (MarkwonPlugin plugin : plugins) {
             plugin.beforeSetText(textView, markdown);
diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonPlugin.java b/markwon/src/main/java/ru/noties/markwon/MarkwonPlugin.java
index 910b044e..fd99b4dd 100644
--- a/markwon/src/main/java/ru/noties/markwon/MarkwonPlugin.java
+++ b/markwon/src/main/java/ru/noties/markwon/MarkwonPlugin.java
@@ -1,6 +1,7 @@
 package ru.noties.markwon;
 
 import android.support.annotation.NonNull;
+import android.text.Spanned;
 import android.widget.TextView;
 
 import org.commonmark.node.Node;
@@ -8,38 +9,137 @@ import org.commonmark.parser.Parser;
 
 import ru.noties.markwon.core.MarkwonTheme;
 import ru.noties.markwon.image.AsyncDrawableLoader;
+import ru.noties.markwon.image.MediaDecoder;
+import ru.noties.markwon.image.SchemeHandler;
 
 /**
+ * Class represents a plugin (extension) to Markwon to configure how parsing and rendering
+ * of markdown is carried on.
+ *
+ * @see AbstractMarkwonPlugin
+ * @see ru.noties.markwon.core.CorePlugin
+ * @see ru.noties.markwon.image.ImagesPlugin
  * @since 3.0.0
  */
 public interface MarkwonPlugin {
 
+    /**
+     * Method to configure <code>org.commonmark.parser.Parser</code> (for example register custom
+     * extension, etc).
+     */
     void configureParser(@NonNull Parser.Builder builder);
 
+    /**
+     * Modify {@link MarkwonTheme} that is used for rendering of markdown.
+     *
+     * @see MarkwonTheme
+     * @see MarkwonTheme.Builder
+     */
     void configureTheme(@NonNull MarkwonTheme.Builder builder);
 
+    /**
+     * Configure image loading functionality. For example add new content-types
+     * {@link AsyncDrawableLoader.Builder#addMediaDecoder(String, MediaDecoder)}, a transport
+     * layer (network, file, etc) {@link AsyncDrawableLoader.Builder#addSchemeHandler(String, SchemeHandler)}
+     * or modify existing properties.
+     *
+     * @see AsyncDrawableLoader
+     * @see AsyncDrawableLoader.Builder
+     */
     void configureImages(@NonNull AsyncDrawableLoader.Builder builder);
 
+    /**
+     * Configure {@link MarkwonConfiguration}
+     *
+     * @see MarkwonConfiguration
+     * @see MarkwonConfiguration.Builder
+     */
     void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder);
 
+    /**
+     * Configure {@link MarkwonVisitor} to accept new node types or override already registered nodes.
+     *
+     * @see MarkwonVisitor
+     * @see MarkwonVisitor.Builder
+     */
     void configureVisitor(@NonNull MarkwonVisitor.Builder builder);
 
+    /**
+     * Configure {@link MarkwonSpansFactory} to change what spans are used for certain node types.
+     *
+     * @see MarkwonSpansFactory
+     * @see MarkwonSpansFactory.Builder
+     */
     void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder);
 
     // can be used to configure own properties and use between plugins
+
+    /**
+     * A method to store some arbitrary data in {@link RenderProps}. Although it won\'t make
+     * much sense to use existing {@link Prop} keys for {@link SpanFactory}, it can be helpful
+     * to establish a communication channel between multiple plugins in decoupled way (provide
+     * some initial properties for example or indicate that certain plugin is registered).
+     * <p>
+     * This method will be called before <em>each</em> rendering step (after rendering {@link RenderProps}
+     * will be cleared. This method <strong>won\'t</strong> be called during initialization stage.
+     *
+     * @see RenderProps
+     */
     void configureRenderProps(@NonNull RenderProps renderProps);
 
+    /**
+     * Process input markdown and return new string to be used in parsing stage further.
+     * Can be described as <code>pre-processing</code> of markdown String.
+     *
+     * @param markdown String to process
+     * @return processed markdown String
+     */
     @NonNull
     String processMarkdown(@NonNull String markdown);
 
+    /**
+     * This method will be called <strong>before</strong> rendering will occur thus making possible
+     * to <code>post-process</code> parsed node (make changes for example).
+     *
+     * @param node root parsed org.commonmark.node.Node
+     */
     void beforeRender(@NonNull Node node);
 
+    /**
+     * This method will be called <strong>after</strong> rendering (but before applying markdown to a
+     * TextView, if such action will happen). It can be used to clean some
+     * internal state, or trigger certain action. Please note that modifying <code>node</code> won\'t
+     * have any effect as it has been already <i>visited</i> at this stage.
+     *
+     * @param node    root parsed org.commonmark.node.Node
+     * @param visitor {@link MarkwonVisitor} instance used to render markdown
+     */
     void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor);
 
-    void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown);
+    /**
+     * This method will be called <strong>before</strong> calling <code>TextView#setText</code>.
+     * <p>
+     * It can be useful to prepare a TextView for markdown. For example {@link ru.noties.markwon.image.ImagesPlugin}
+     * uses this method to unregister previously registered {@link ru.noties.markwon.image.AsyncDrawableSpan}
+     * (if there are such spans in this TextView at this point). Or {@link ru.noties.markwon.core.CorePlugin}
+     * which measures ordered list numbers
+     *
+     * @param textView TextView to which <code>markdown</code> will be applied
+     * @param markdown Parsed markdown
+     */
+    void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown);
 
-    // this method do not receive markdown like `beforeSetText` does because at this
-    // point TextView already has markdown set and to manipulate spans one must
-    // request them from TextView (getText())
+    /**
+     * This method will be called <strong>after</strong> markdown was applied.
+     * <p>
+     * It can be useful to trigger certain action on spans/textView. For example {@link ru.noties.markwon.image.ImagesPlugin}
+     * uses this method to register {@link ru.noties.markwon.image.AsyncDrawableSpan} and start
+     * asynchronously loading images.
+     * <p>
+     * Unlike {@link #beforeSetText(TextView, Spanned)} this method does not receive parsed markdown
+     * as at this point spans must be queried by calling <code>TextView#getText#getSpans</code>.
+     *
+     * @param textView TextView to which markdown was applied
+     */
     void afterSetText(@NonNull TextView textView);
 }
diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactory.java b/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactory.java
index 748ebd57..70545066 100644
--- a/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactory.java
+++ b/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactory.java
@@ -6,27 +6,46 @@ import android.support.annotation.Nullable;
 import org.commonmark.node.Node;
 
 /**
+ * Class that controls what spans are used for certain Nodes.
+ *
+ * @see SpanFactory
  * @since 3.0.0
  */
 public interface MarkwonSpansFactory {
 
+    /**
+     * Returns registered {@link SpanFactory} or <code>null</code> if a factory for this node type
+     * is not registered. There is {@link #require(Class)} method that will throw an exception
+     * if required {@link SpanFactory} is not registered, thus making return type <code>non-null</code>
+     *
+     * @param node type of the node
+     * @return registered {@link SpanFactory} or null if it\'s not registered
+     * @see #require(Class)
+     */
     @Nullable
-    <N extends Node, F extends SpanFactory> F get(@NonNull Class<N> node);
+    <N extends Node> SpanFactory get(@NonNull Class<N> node);
 
+    /**
+     * @see #get(Class)
+     * @see #require(Node)
+     */
     @Nullable
-    <N extends Node, F extends SpanFactory> F get(@NonNull N node);
+    <N extends Node> SpanFactory get(@NonNull N node);
 
     @NonNull
-    <N extends Node, F extends SpanFactory> F require(@NonNull Class<N> node);
+    <N extends Node> SpanFactory require(@NonNull Class<N> node);
 
+    /**
+     * @see #require(Class)
+     */
     @NonNull
-    <N extends Node, F extends SpanFactory> F require(@NonNull N node);
+    <N extends Node> SpanFactory require(@NonNull N node);
 
 
     interface Builder {
 
         @NonNull
-        <N extends Node, F extends SpanFactory> Builder setFactory(@NonNull Class<N> node, @NonNull F factory);
+        <N extends Node> Builder setFactory(@NonNull Class<N> node, @NonNull SpanFactory factory);
 
         @NonNull
         MarkwonSpansFactory build();
diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactoryImpl.java b/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactoryImpl.java
index 7bbc8ca8..81b5a0c3 100644
--- a/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactoryImpl.java
+++ b/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactoryImpl.java
@@ -22,21 +22,20 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory {
 
     @Nullable
     @Override
-    public <N extends Node, F extends SpanFactory> F get(@NonNull Class<N> node) {
-        //noinspection unchecked
-        return (F) factories.get(node);
+    public <N extends Node> SpanFactory get(@NonNull Class<N> node) {
+        return factories.get(node);
     }
 
     @Nullable
     @Override
-    public <N extends Node, F extends SpanFactory> F get(@NonNull N node) {
+    public <N extends Node> SpanFactory get(@NonNull N node) {
         return get(node.getClass());
     }
 
     @NonNull
     @Override
-    public <N extends Node, F extends SpanFactory> F require(@NonNull Class<N> node) {
-        final F f = get(node);
+    public <N extends Node> SpanFactory require(@NonNull Class<N> node) {
+        final SpanFactory f = get(node);
         if (f == null) {
             throw new NullPointerException();
         }
@@ -45,8 +44,8 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory {
 
     @NonNull
     @Override
-    public <N extends Node, F extends SpanFactory> F require(@NonNull N node) {
-        final F f = get(node);
+    public <N extends Node> SpanFactory require(@NonNull N node) {
+        final SpanFactory f = get(node);
         if (f == null) {
             throw new NullPointerException();
         }
@@ -60,7 +59,7 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory {
 
         @NonNull
         @Override
-        public <N extends Node, F extends SpanFactory> Builder setFactory(@NonNull Class<N> node, @NonNull F factory) {
+        public <N extends Node> Builder setFactory(@NonNull Class<N> node, @NonNull SpanFactory factory) {
             factories.put(node, factory);
             return this;
         }
diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonVisitor.java b/markwon/src/main/java/ru/noties/markwon/MarkwonVisitor.java
index 81d3138f..aec63444 100644
--- a/markwon/src/main/java/ru/noties/markwon/MarkwonVisitor.java
+++ b/markwon/src/main/java/ru/noties/markwon/MarkwonVisitor.java
@@ -7,16 +7,29 @@ import org.commonmark.node.Node;
 import org.commonmark.node.Visitor;
 
 /**
+ * Configurable visitor of parsed markdown. Allows visiting certain (registered) nodes without
+ * need to create own instance of this class.
+ *
+ * @see Builder#on(Class, NodeVisitor)
+ * @see MarkwonPlugin#configureVisitor(Builder)
  * @since 3.0.0
  */
 public interface MarkwonVisitor extends Visitor {
 
+    /**
+     * @see Builder#on(Class, NodeVisitor)
+     */
     interface NodeVisitor<N extends Node> {
         void visit(@NonNull MarkwonVisitor visitor, @NonNull N n);
     }
 
     interface Builder {
 
+        /**
+         * @param node        to register
+         * @param nodeVisitor {@link NodeVisitor} to be used or null to ignore previously registered
+         *                    visitor for this node
+         */
         @NonNull
         <N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<? super N> nodeVisitor);
 
@@ -33,26 +46,86 @@ public interface MarkwonVisitor extends Visitor {
     @NonNull
     SpannableBuilder builder();
 
+    /**
+     * Visits all children of supplied node.
+     *
+     * @param node to visit
+     */
     void visitChildren(@NonNull Node node);
 
+    /**
+     * Executes a check if there is further content available.
+     *
+     * @param node to check
+     * @return boolean indicating if there are more nodes after supplied one
+     */
     boolean hasNext(@NonNull Node node);
 
+    /**
+     * This method <strong>ensures</strong> that further content will start at a new line. If current
+     * last character is already a new line, then it won\'t do anything.
+     */
     void ensureNewLine();
 
+    /**
+     * This method inserts a new line without any condition checking (unlike {@link #ensureNewLine()}).
+     */
     void forceNewLine();
 
+    /**
+     * Helper method to call <code>builder().length()</code>
+     *
+     * @return current length of underlying {@link SpannableBuilder}
+     */
     int length();
 
+    /**
+     * Sets <code>spans</code> to underlying {@link SpannableBuilder} from <em>start</em>
+     * to <em>{@link SpannableBuilder#length()}</em>.
+     *
+     * @param start start position of spans
+     * @param spans to apply
+     */
     void setSpans(int start, @Nullable Object spans);
 
-    // will automatically obtain SpanFactory instance and use it, it no SpanFactory is registered,
-    // will throw, if not desired use setSpansForNodeOptional
+    /**
+     * Helper method to obtain and apply spans for supplied Node. Internally queries {@link SpanFactory}
+     * for the node (via {@link MarkwonSpansFactory#require(Node)} thus throwing an exception
+     * if there is no {@link SpanFactory} registered for the node).
+     *
+     * @param node  to retrieve {@link SpanFactory} for
+     * @param start start position for further {@link #setSpans(int, Object)} call
+     * @see #setSpansForNodeOptional(Node, int)
+     */
     <N extends Node> void setSpansForNode(@NonNull N node, int start);
 
+    /**
+     * The same as {@link #setSpansForNode(Node, int)} but can be used in situations when there is
+     * no access to a Node instance (for example in HTML rendering which doesn\'t have markdown Nodes).
+     *
+     * @see #setSpansForNode(Node, int)
+     */
     <N extends Node> void setSpansForNode(@NonNull Class<N> node, int start);
 
     // does not throw if there is no SpanFactory registered for this node
+
+    /**
+     * Helper method to apply spans from a {@link SpanFactory} <b>if</b> it\'s registered in
+     * {@link MarkwonSpansFactory} instance. Otherwise ignores this call (no spans will be applied).
+     * If there is a need to ensure that specified <code>node</code> has a {@link SpanFactory} registered,
+     * then {@link #setSpansForNode(Node, int)} can be used. {@link #setSpansForNode(Node, int)} internally
+     * uses {@link MarkwonSpansFactory#require(Node)}. This method uses {@link MarkwonSpansFactory#get(Node)}.
+     *
+     * @see #setSpansForNode(Node, int)
+     */
     <N extends Node> void setSpansForNodeOptional(@NonNull N node, int start);
 
+    /**
+     * The same as {@link #setSpansForNodeOptional(Node, int)} but can be used in situations when
+     * there is no access to a Node instance (for example in HTML rendering).
+     *
+     * @see #setSpansForNodeOptional(Node, int)
+     */
+    @SuppressWarnings("unused")
     <N extends Node> void setSpansForNodeOptional(@NonNull Class<N> node, int start);
 }
diff --git a/markwon/src/main/java/ru/noties/markwon/Prop.java b/markwon/src/main/java/ru/noties/markwon/Prop.java
index a930288f..caacbfab 100644
--- a/markwon/src/main/java/ru/noties/markwon/Prop.java
+++ b/markwon/src/main/java/ru/noties/markwon/Prop.java
@@ -4,9 +4,11 @@ import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
 /**
- * Class to hold data in {@link RenderProps}
+ * Class to hold data in {@link RenderProps}. Represents a certain <em>property</em>.
  *
  * @param <T> represents the type that this instance holds
+ * @see #of(String)
+ * @see #of(Class, String)
  * @since 3.0.0
  */
 public final class Prop<T> {
diff --git a/markwon/src/main/java/ru/noties/markwon/RenderProps.java b/markwon/src/main/java/ru/noties/markwon/RenderProps.java
index b939acc4..e28edbaf 100644
--- a/markwon/src/main/java/ru/noties/markwon/RenderProps.java
+++ b/markwon/src/main/java/ru/noties/markwon/RenderProps.java
@@ -17,4 +17,6 @@ public interface RenderProps {
     <T> void set(@NonNull Prop<T> prop, @Nullable T value);
 
     <T> void clear(@NonNull Prop<T> prop);
+
+    void clearAll();
 }
diff --git a/markwon/src/main/java/ru/noties/markwon/RenderPropsImpl.java b/markwon/src/main/java/ru/noties/markwon/RenderPropsImpl.java
index 81605ee9..75b6ac4b 100644
--- a/markwon/src/main/java/ru/noties/markwon/RenderPropsImpl.java
+++ b/markwon/src/main/java/ru/noties/markwon/RenderPropsImpl.java
@@ -45,6 +45,7 @@ public class RenderPropsImpl implements RenderProps {
         values.remove(prop);
     }
 
+    @Override
     public void clearAll() {
         values.clear();
     }
diff --git a/markwon/src/main/java/ru/noties/markwon/core/CorePlugin.java b/markwon/src/main/java/ru/noties/markwon/core/CorePlugin.java
index 47024866..187f0173 100644
--- a/markwon/src/main/java/ru/noties/markwon/core/CorePlugin.java
+++ b/markwon/src/main/java/ru/noties/markwon/core/CorePlugin.java
@@ -3,6 +3,7 @@ package ru.noties.markwon.core;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
+import android.text.Spanned;
 import android.widget.TextView;
 
 import org.commonmark.node.BlockQuote;
@@ -104,7 +105,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
     }
 
     @Override
-    public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) {
+    public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
         OrderedListItemSpan.measure(textView, markdown);
     }
 
@@ -123,7 +124,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
             public void visit(@NonNull MarkwonVisitor visitor, @NonNull StrongEmphasis strongEmphasis) {
                 final int length = visitor.length();
                 visitor.visitChildren(strongEmphasis);
-                visitor.setSpansForNode(strongEmphasis, length);
+                visitor.setSpansForNodeOptional(strongEmphasis, length);
             }
         });
     }
@@ -134,7 +135,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emphasis emphasis) {
                 final int length = visitor.length();
                 visitor.visitChildren(emphasis);
-                visitor.setSpansForNode(emphasis, length);
+                visitor.setSpansForNodeOptional(emphasis, length);
             }
         });
     }
@@ -149,7 +150,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
                 final int length = visitor.length();
 
                 visitor.visitChildren(blockQuote);
-                visitor.setSpansForNode(blockQuote, length);
+                visitor.setSpansForNodeOptional(blockQuote, length);
 
                 if (visitor.hasNext(blockQuote)) {
                     visitor.ensureNewLine();
@@ -173,7 +174,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
                         .append(code.getLiteral())
                         .append('\u00a0');
 
-                visitor.setSpansForNode(code, length);
+                visitor.setSpansForNodeOptional(code, length);
             }
         });
     }
@@ -215,7 +216,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
 
         visitor.builder().append('\u00a0');
 
-        visitor.setSpansForNode(node, length);
+        visitor.setSpansForNodeOptional(node, length);
 
         if (visitor.hasNext(node)) {
             visitor.ensureNewLine();
@@ -260,7 +261,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
                     CoreProps.BULLET_LIST_ITEM_LEVEL.set(visitor.renderProps(), listLevel(listItem));
                 }
 
-                visitor.setSpansForNode(listItem, length);
+                visitor.setSpansForNodeOptional(listItem, length);
 
                 if (visitor.hasNext(listItem)) {
                     visitor.ensureNewLine();
@@ -293,7 +294,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
                 // without space it won't render
                 visitor.builder().append('\u00a0');
 
-                visitor.setSpansForNode(thematicBreak, length);
+                visitor.setSpansForNodeOptional(thematicBreak, length);
 
                 if (visitor.hasNext(thematicBreak)) {
                     visitor.ensureNewLine();
@@ -315,7 +316,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
 
                 CoreProps.HEADING_LEVEL.set(visitor.renderProps(), heading.getLevel());
 
-                visitor.setSpansForNode(heading, length);
+                visitor.setSpansForNodeOptional(heading, length);
 
                 if (visitor.hasNext(heading)) {
                     visitor.ensureNewLine();
@@ -399,7 +400,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
 
                 CoreProps.LINK_DESTINATION.set(visitor.renderProps(), destination);
 
-                visitor.setSpansForNode(link, length);
+                visitor.setSpansForNodeOptional(link, length);
             }
         });
     }
diff --git a/markwon/src/main/java/ru/noties/markwon/core/MarkwonTheme.java b/markwon/src/main/java/ru/noties/markwon/core/MarkwonTheme.java
index 347a2321..908e200f 100644
--- a/markwon/src/main/java/ru/noties/markwon/core/MarkwonTheme.java
+++ b/markwon/src/main/java/ru/noties/markwon/core/MarkwonTheme.java
@@ -16,6 +16,22 @@ import java.util.Locale;
 import ru.noties.markwon.utils.ColorUtils;
 import ru.noties.markwon.utils.Dip;
 
+/**
+ * Class to hold <i>theming</i> information for rending of markdown.
+ * <p>
+ * Since version 3.0.0 this class should be considered as <em>CoreTheme</em> as it\'s
+ * information holds data for core features only. But based on this other components can still use it
+ * to display markdown consistently.
+ * <p>
+ * Since version 3.0.0 this class should not be instantiated manually. Instead a {@link ru.noties.markwon.MarkwonPlugin}
+ * should be used: {@link ru.noties.markwon.MarkwonPlugin#configureTheme(Builder)}
+ * <p>
+ * Since version 3.0.0 properties related to <em>strike-through</em>, <em>tables</em> and <em>HTML</em>
+ * are moved to specific plugins in independent artifacts
+ *
+ * @see CorePlugin
+ * @see ru.noties.markwon.MarkwonPlugin#configureTheme(Builder)
+ */
 @SuppressWarnings("WeakerAccess")
 public class MarkwonTheme {
 
@@ -41,12 +57,28 @@ public class MarkwonTheme {
      * @see #builderWithDefaults(Context)
      * @see #builder(MarkwonTheme)
      * @since 1.0.0
+     * @deprecated 3.0.0
      */
     @NonNull
+    @Deprecated
     public static Builder builder() {
         return new Builder();
     }
 
+    /**
+     * Create an <strong>empty</strong> instance of {@link Builder} with no default values applied
+     * <p>
+     * Since version 3.0.0 manual construction of {@link MarkwonTheme} is not required, instead a
+     * {@link ru.noties.markwon.MarkwonPlugin#configureTheme(Builder)} should be used in order
+     * to change certain theme properties
+     *
+     * @since 3.0.0
+     */
+    @NonNull
+    public static Builder builderNoDefaults() {
+        return new Builder();
+    }
+
     /**
      * Factory method to create a {@link Builder} instance and initialize it with values
      * from supplied {@link MarkwonTheme}
@@ -549,6 +581,7 @@ public class MarkwonTheme {
          * @return self
          * @since 1.1.0
          */
+        @SuppressWarnings("UnusedReturnValue")
         @NonNull
         public Builder headingTextSizeMultipliers(@Size(6) @NonNull float[] headingTextSizeMultipliers) {
             this.headingTextSizeMultipliers = headingTextSizeMultipliers;
diff --git a/markwon/src/main/java/ru/noties/markwon/image/ImagesPlugin.java b/markwon/src/main/java/ru/noties/markwon/image/ImagesPlugin.java
index 7e91f288..dc51504f 100644
--- a/markwon/src/main/java/ru/noties/markwon/image/ImagesPlugin.java
+++ b/markwon/src/main/java/ru/noties/markwon/image/ImagesPlugin.java
@@ -2,6 +2,7 @@ package ru.noties.markwon.image;
 
 import android.content.Context;
 import android.support.annotation.NonNull;
+import android.text.Spanned;
 import android.widget.TextView;
 
 import org.commonmark.node.Image;
@@ -15,6 +16,7 @@ import ru.noties.markwon.MarkwonConfiguration;
 import ru.noties.markwon.MarkwonSpansFactory;
 import ru.noties.markwon.MarkwonVisitor;
 import ru.noties.markwon.RenderProps;
+import ru.noties.markwon.SpanFactory;
 import ru.noties.markwon.image.data.DataUriSchemeHandler;
 import ru.noties.markwon.image.file.FileSchemeHandler;
 import ru.noties.markwon.image.network.NetworkSchemeHandler;
@@ -68,6 +70,13 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
             @Override
             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Image image) {
 
+                // if there is no image spanFactory, ignore
+                final SpanFactory spanFactory = visitor.configuration().spansFactory().get(image);
+                if (spanFactory == null) {
+                    visitor.visitChildren(image);
+                    return;
+                }
+
                 final int length = visitor.length();
 
                 visitor.visitChildren(image);
@@ -86,22 +95,22 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
                         .urlProcessor()
                         .process(image.getDestination());
 
-                final RenderProps context = visitor.renderProps();
+                final RenderProps props = visitor.renderProps();
 
                 // apply image properties
                 // Please note that we explicitly set IMAGE_SIZE to null as we do not clear
                 // properties after we applied span (we could though)
-                ImageProps.DESTINATION.set(context, destination);
-                ImageProps.REPLACEMENT_TEXT_IS_LINK.set(context, link);
-                ImageProps.IMAGE_SIZE.set(context, null);
+                ImageProps.DESTINATION.set(props, destination);
+                ImageProps.REPLACEMENT_TEXT_IS_LINK.set(props, link);
+                ImageProps.IMAGE_SIZE.set(props, null);
 
-                visitor.setSpansForNode(image, length);
+                visitor.setSpans(length, spanFactory.getSpans(configuration, props));
             }
         });
     }
 
     @Override
-    public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) {
+    public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
         AsyncDrawableScheduler.unschedule(textView);
     }
 
diff --git a/markwon/src/test/java/ru/noties/markwon/core/suite/BaseSuiteTest.java b/markwon/src/test/java/ru/noties/markwon/core/suite/BaseSuiteTest.java
index f91f2f13..98734db7 100644
--- a/markwon/src/test/java/ru/noties/markwon/core/suite/BaseSuiteTest.java
+++ b/markwon/src/test/java/ru/noties/markwon/core/suite/BaseSuiteTest.java
@@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.Spanned;
 
+import org.apache.commons.io.IOUtils;
 import org.commonmark.node.BlockQuote;
 import org.commonmark.node.Code;
 import org.commonmark.node.Emphasis;
@@ -18,7 +19,8 @@ import org.commonmark.node.StrongEmphasis;
 import org.commonmark.node.ThematicBreak;
 import org.robolectric.RuntimeEnvironment;
 
-import java.util.Collections;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -32,7 +34,6 @@ import ru.noties.markwon.core.CorePlugin;
 import ru.noties.markwon.core.CoreProps;
 import ru.noties.markwon.test.TestSpan;
 import ru.noties.markwon.test.TestSpanMatcher;
-import ru.noties.markwon.test.TestUtil;
 
 import static ru.noties.markwon.test.TestSpan.args;
 import static ru.noties.markwon.test.TestSpan.span;
@@ -62,7 +63,11 @@ abstract class BaseSuiteTest {
 
   @NonNull
   private String read(@NonNull String name) {
-    return TestUtil.read(this, "tests/" + name);
+    try {
+      return IOUtils.resourceToString("tests/" + name, StandardCharsets.UTF_8, getClass().getClassLoader());
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
   }
 
   @NonNull
@@ -160,12 +165,7 @@ abstract class BaseSuiteTest {
     @Nullable
     @Override
     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
-      return span(name, extractArgs(props));
-    }
-
-    @NonNull
-    Map<String, Object> extractArgs(@NonNull RenderProps props) {
-      return Collections.emptyMap();
+      return span(name);
     }
   }
 }
diff --git a/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java b/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java
index 732a182f..7e0a2af2 100644
--- a/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java
+++ b/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java
@@ -13,6 +13,7 @@ import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
 
@@ -74,7 +75,7 @@ public class SyntaxHighlightTest {
         };
 
         final MarkwonSpansFactory spansFactory = mock(MarkwonSpansFactory.class);
-        when(spansFactory.require(any(FencedCodeBlock.class))).thenReturn(new SpanFactory() {
+        when(spansFactory.get(any(FencedCodeBlock.class))).thenReturn(new SpanFactory() {
             @Override
             public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
                 return codeSpan;
@@ -103,7 +104,7 @@ public class SyntaxHighlightTest {
         final FencedCodeBlock fencedCodeBlock = new FencedCodeBlock();
         fencedCodeBlock.setLiteral("{code}");
 
-        CorePluginBridge.visitCodeBlock(visitor, null, "{code}", fencedCodeBlock);
+        CorePluginBridge.visitCodeBlock(visitor, null, fencedCodeBlock.getLiteral(), fencedCodeBlock);
 
         final int end = builder.length();
 
@@ -115,7 +116,7 @@ public class SyntaxHighlightTest {
 
         // each character + code span
         final int length = fencedCodeBlock.getLiteral().length() + 1;
-        assertEquals(length, spans.length);
+        assertEquals(Arrays.toString(spans), length, spans.length);
         assertEquals(codeSpan, spans[0]);
 
         // each character
diff --git a/settings.gradle b/settings.gradle
index ede0753a..d5f56c45 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -11,6 +11,5 @@ include ':app',
         ':markwon-html',
         ':markwon-view',
         ':markwon-test-span',
-        ':markwon-test-util',
         ':sample-custom-extension',
         ':sample-latex-math'