diff --git a/source/_includes/compatibility.liquid b/source/_includes/compatibility.liquid
index 9d106fa94..8e8c9e005 100644
--- a/source/_includes/compatibility.liquid
+++ b/source/_includes/compatibility.liquid
@@ -34,6 +34,6 @@
{%- if details | strip -%}
- {{ details | markdown }}
+ {% if useMarkdown %}{{ details | markdown }}{% else %}{{ details }}{% endif %}
{%- endif -%}
diff --git a/source/code-snippets/example-each-list.liquid b/source/code-snippets/example-each-list.liquid
new file mode 100644
index 000000000..d64e80bd9
--- /dev/null
+++ b/source/code-snippets/example-each-list.liquid
@@ -0,0 +1,19 @@
+{% codeExample 'each-list' %}
+$sizes: 40px, 50px, 80px;
+
+@each $size in $sizes {
+ .icon-#{$size} {
+ font-size: $size;
+ height: $size;
+ width: $size;
+ }
+}
+===
+$sizes: 40px, 50px, 80px
+
+@each $size in $sizes
+ .icon-#{$size}
+ font-size: $size
+ height: $size
+ width: $size
+{% endcodeExample %}
diff --git a/source/code-snippets/example-each-map.liquid b/source/code-snippets/example-each-map.liquid
new file mode 100644
index 000000000..76dfba284
--- /dev/null
+++ b/source/code-snippets/example-each-map.liquid
@@ -0,0 +1,19 @@
+{% codeExample 'each-map' %}
+$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f");
+
+@each $name, $glyph in $icons {
+ .icon-#{$name}:before {
+ display: inline-block;
+ font-family: "Icon Font";
+ content: $glyph;
+ }
+}
+===
+$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f")
+
+@each $name, $glyph in $icons
+ .icon-#{$name}:before
+ display: inline-block
+ font-family: "Icon Font"
+ content: $glyph
+{% endcodeExample %}
diff --git a/source/code-snippets/example-if.liquid b/source/code-snippets/example-if.liquid
new file mode 100644
index 000000000..0832a6bb9
--- /dev/null
+++ b/source/code-snippets/example-if.liquid
@@ -0,0 +1,29 @@
+{% codeExample 'example-if' %}
+@mixin avatar($size, $circle: false) {
+ width: $size;
+ height: $size;
+
+ @if $circle {
+ border-radius: $size / 2;
+ }
+}
+
+.square-av {
+ @include avatar(100px, $circle: false);
+}
+.circle-av {
+ @include avatar(100px, $circle: true);
+}
+===
+@mixin avatar($size, $circle: false)
+ width: $size
+ height: $size
+
+ @if $circle
+ border-radius: $size / 2
+
+.square-av
+ @include avatar(100px, $circle: false)
+.circle-av
+ @include avatar(100px, $circle: true)
+{% endcodeExample %}
diff --git a/source/documentation/at-rules/at-root.liquid b/source/documentation/at-rules/at-root.liquid
new file mode 100644
index 000000000..e9a2de1ee
--- /dev/null
+++ b/source/documentation/at-rules/at-root.liquid
@@ -0,0 +1,79 @@
+---
+title: "@at-root"
+introduction: >
+ The `@at-root` rule is usually written `@at-root { ... }` and
+ causes everything within it to be emitted at the root of the document instead
+ of using the normal nesting. It's most often used when doing [advanced
+ nesting](/documentation/style-rules/parent-selector#advanced-nesting) with the
+ [SassScript parent
+ selector](/documentation/style-rules/parent-selector#in-sassscript) and
+ [selector functions](/documentation/modules/selector).
+---
+
+{% render 'code-snippets/example-advanced-nesting' %}
+
+{% markdown %}
+The `@at-root` rule is necessary here because Sass doesn't know what
+interpolation was used to generate a selector when it's performing selector
+nesting. This means it will automatically add the outer selector to the inner
+selector *even if* you used `&` as a SassScript expression. The `@at-root`
+explicitly tells Sass not to include the outer selector.
+
+{% funFact %}
+The `@at-root` rule can also be written `@at-root { ... }` to put multiple style
+rules at the root of the document. In fact, `@at-root { ... }` is
+just a shorthand for `@at-root { { ... } }`!
+{% endfunFact %}
+
+## Beyond Style Rules
+
+On its own, `@at-root` only gets rid of [style rules][]. Any at-rules like
+[`@media`][] or [`@supports`][] will be left in. If this isn't what you want,
+though, you can control exactly what it includes or excludes using syntax like
+[media query features][], written `@at-root (with: ) { ... }` or
+`@at-root (without: ) { ... }`. The `(without: ...)` query tells Sass
+which rules should be excluded; the `(with: ...)` query excludes all rules
+*except* those that are listed.
+
+[style rules]: /documentation/style-rules
+[`@media`]: /documentation/at-rules/css#media
+[`@supports`]: /documentation/at-rules/css#supports
+[media query features]: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#Targeting_media_features
+{% endmarkdown %}
+
+{% codeExample 'at-root' %}
+@media print {
+ .page {
+ width: 8in;
+
+ @at-root (without: media) {
+ color: #111;
+ }
+
+ @at-root (with: rule) {
+ font-size: 1.2em;
+ }
+ }
+}
+===
+@media print
+ .page
+ width: 8in
+
+ @at-root (without: media)
+ color: #111
+
+
+ @at-root (with: rule)
+ font-size: 1.2em
+{% endcodeExample %}
+
+{% markdown %}
+In addition to the names of at-rules, there are two special values that can be
+used in queries:
+
+* `rule` refers to style rules. For example, `@at-root (with: rule)` excludes
+ all at-rules but preserves style rules.
+
+* `all` refers to all at-rules *and* style rules should be excluded.
+{% endmarkdown %}
diff --git a/source/documentation/at-rules/control/each.liquid b/source/documentation/at-rules/control/each.liquid
new file mode 100644
index 000000000..3dbcd4da7
--- /dev/null
+++ b/source/documentation/at-rules/control/each.liquid
@@ -0,0 +1,73 @@
+---
+title: "@each"
+introduction: >
+ The `@each` rule makes it easy to emit styles or evaluate code for each
+ element of a [list](/documentation/values/lists) or each pair in a
+ [map](/documentation/values/maps). It’s great for repetitive styles that only
+ have a few variations between them. It’s usually written `@each in
+ { ... }`, where the
+ [expression](/documentation/syntax/structure#expressions) returns a list. The
+ block is evaluated for each element of the list in turn, which is assigned to
+ the given variable name.
+---
+
+{% render 'code-snippets/example-each-list' %}
+
+{% markdown %}
+## With Maps
+
+You can also use `@each` to iterate over every key/value pair in a map by
+writing it `@each , in { ... }`. The key is
+assigned to the first variable name, and the element is assigned to the second.
+{% endmarkdown %}
+
+{% render 'code-snippets/example-each-map' %}
+
+{% markdown %}
+## Destructuring
+
+If you have a list of lists, you can use `@each` to automatically assign
+variables to each of the values from the inner lists by writing it
+`@each in { ... }`. This is known as *destructuring*,
+since the variables match the structure of the inner lists. Each variable name
+is assigned to the value at the corresponding position in the list, or
+[`null`][] if the list doesn't have enough values.
+
+[`null`]: /documentation/values/null
+{% endmarkdown %}
+
+{% codeExample 'each' %}
+$icons:
+ "eye" "\f112" 12px,
+ "start" "\f12e" 16px,
+ "stop" "\f12f" 10px;
+
+@each $name, $glyph, $size in $icons {
+ .icon-#{$name}:before {
+ display: inline-block;
+ font-family: "Icon Font";
+ content: $glyph;
+ font-size: $size;
+ }
+}
+===
+$icons: "eye" "\f112" 12px, "start" "\f12e" 16px, "stop" "\f12f" 10px
+
+
+
+
+@each $name, $glyph, $size in $icons
+ .icon-#{$name}:before
+ display: inline-block
+ font-family: "Icon Font"
+ content: $glyph
+ font-size: $size
+{% endcodeExample %}
+
+{% funFact %}
+Because `@each` supports destructuring and [maps count as lists of lists][],
+`@each`'s map support works without needing special support for maps in
+particular.
+
+[maps count as lists of lists]: /documentation/values/maps
+{% endfunFact %}
diff --git a/source/documentation/at-rules/control/for.liquid b/source/documentation/at-rules/control/for.liquid
new file mode 100644
index 000000000..7cf6f7734
--- /dev/null
+++ b/source/documentation/at-rules/control/for.liquid
@@ -0,0 +1,27 @@
+---
+title: "@for"
+introduction: >
+ The `@for` rule, written `@for from to {
+ ... }` or `@for from through { ... }`,
+ counts up or down from one number (the result of the first
+ [expression](/documentation/syntax/structure#expressions)) to another (the
+ result of the second) and evaluates a block for each number in between. Each
+ number along the way is assigned to the given variable name. If `to` is used,
+ the final number is excluded; if `through` is used, it's included.
+---
+
+{% codeExample 'for' %}
+$base-color: #036;
+
+@for $i from 1 through 3 {
+ ul:nth-child(3n + #{$i}) {
+ background-color: lighten($base-color, $i * 5%);
+ }
+}
+===
+$base-color: #036
+
+@for $i from 1 through 3
+ ul:nth-child(3n + #{$i})
+ background-color: lighten($base-color, $i * 5%)
+{% endcodeExample %}
diff --git a/source/documentation/at-rules/control/if.liquid b/source/documentation/at-rules/control/if.liquid
new file mode 100644
index 000000000..a23395206
--- /dev/null
+++ b/source/documentation/at-rules/control/if.liquid
@@ -0,0 +1,149 @@
+---
+title: "@if and @else"
+table_of_contents: true
+introduction: >
+ The `@if` rule is written `@if { ... }`, and it controls whether
+ or not its block gets evaluated (including emitting any styles as CSS). The
+ [expression](/documentation/syntax/structure#expressions) usually returns
+ either [`true` or `false`](/documentation/values/booleans)—if the expression
+ returns `true`, the block is evaluated, and if the expression returns `false`
+ it’s not.
+---
+
+{% render 'code-snippets/example-if' %}
+
+{% markdown %}
+## `@else`
+
+An `@if` rule can optionally be followed by an `@else` rule, written
+`@else { ... }`. This rule's block is evaluated if the `@if` expression returns
+`false`.
+{% endmarkdown %}
+
+{% codeExample 'if' %}
+$light-background: #f2ece4;
+$light-text: #036;
+$dark-background: #6b717f;
+$dark-text: #d2e1dd;
+
+@mixin theme-colors($light-theme: true) {
+ @if $light-theme {
+ background-color: $light-background;
+ color: $light-text;
+ } @else {
+ background-color: $dark-background;
+ color: $dark-text;
+ }
+}
+
+.banner {
+ @include theme-colors($light-theme: true);
+ body.dark & {
+ @include theme-colors($light-theme: false);
+ }
+}
+===
+$light-background: #f2ece4
+$light-text: #036
+$dark-background: #6b717f
+$dark-text: #d2e1dd
+
+@mixin theme-colors($light-theme: true)
+ @if $light-theme
+ background-color: $light-background
+ color: $light-text
+ @else
+ background-color: $dark-background
+ color: $dark-text
+
+
+
+.banner
+ @include theme-colors($light-theme: true)
+ body.dark &
+ @include theme-colors($light-theme: false)
+{% endcodeExample %}
+
+{% markdown %}
+Conditional expressions may contain [boolean operators][] (`and`, `or`, `not`).
+
+[boolean operators]: /documentation/operators/boolean
+
+### `@else if`
+
+You can also choose whether to evaluate an `@else` rule's block by writing it
+`@else if { ... }`. If you do, the block is evaluated only if the
+preceding `@if`'s expression returns `false` *and* the `@else if`'s expression
+returns `true`.
+
+In fact, you can chain as many `@else if`s as you want after an `@if`. The
+first block in the chain whose expression returns `true` will be evaluated, and
+no others. If there's a plain `@else` at the end of the chain, its block will be
+evaluated if every other block fails.
+{% endmarkdown %}
+
+{% codeExample 'else' %}
+@use "sass:math";
+
+@mixin triangle($size, $color, $direction) {
+ height: 0;
+ width: 0;
+
+ border-color: transparent;
+ border-style: solid;
+ border-width: math.div($size, 2);
+
+ @if $direction == up {
+ border-bottom-color: $color;
+ } @else if $direction == right {
+ border-left-color: $color;
+ } @else if $direction == down {
+ border-top-color: $color;
+ } @else if $direction == left {
+ border-right-color: $color;
+ } @else {
+ @error "Unknown direction #{$direction}.";
+ }
+}
+
+.next {
+ @include triangle(5px, black, right);
+}
+===
+@use "sass:math"
+
+@mixin triangle($size, $color, $direction)
+ height: 0
+ width: 0
+
+ border-color: transparent
+ border-style: solid
+ border-width: math.div($size, 2)
+
+ @if $direction == up
+ border-bottom-color: $color
+ @else if $direction == right
+ border-left-color: $color
+ @else if $direction == down
+ border-top-color: $color
+ @else if $direction == left
+ border-right-color: $color
+ @else
+ @error "Unknown direction #{$direction}."
+
+
+
+.next
+ @include triangle(5px, black, right)
+===
+.next {
+ height: 0;
+ width: 0;
+ border-color: transparent;
+ border-style: solid;
+ border-width: 2.5px;
+ border-left-color: black;
+}
+{% endcodeExample %}
+
+{% render 'documentation/snippets/truthiness-and-falsiness' %}
diff --git a/source/documentation/at-rules/control/index.md b/source/documentation/at-rules/control/index.md
new file mode 100644
index 000000000..8fcde0fae
--- /dev/null
+++ b/source/documentation/at-rules/control/index.md
@@ -0,0 +1,24 @@
+---
+title: Flow Control Rules
+introduction: >
+ Sass provides a number of at-rules that make it possible to control whether
+ styles get emitted, or to emit them multiple times with small variations. They
+ can also be used in [mixins](/documentation/at-rules/mixin) and
+ [functions](/documentation/at-rules/function) to write small algorithms to
+ make writing your Sass easier. Sass supports four flow control rules.
+---
+
+- [`@if`](/documentation/at-rules/control/if) controls whether or not a block is
+ evaluated.
+
+- [`@each`](/documentation/at-rules/control/each) evaluates a block for each
+ element in a [list][] or each pair in a [map][].
+
+- [`@for`](/documentation/at-rules/control/for) evaluates a block a certain
+ number of times.
+
+- [`@while`](/documentation/at-rules/control/while) evaluates a block until a
+ certain condition is met.
+
+[list]: /documentation/values/lists
+[map]: /documentation/values/maps
diff --git a/source/documentation/at-rules/control/while.liquid b/source/documentation/at-rules/control/while.liquid
new file mode 100644
index 000000000..d1f73b048
--- /dev/null
+++ b/source/documentation/at-rules/control/while.liquid
@@ -0,0 +1,55 @@
+---
+title: "@while"
+introduction: >
+ The `@while` rule, written `@while { ... }`, evaluates its block
+ if its [expression](/documentation/syntax/structure#expressions) returns
+ `true`. Then, if its expression still returns `true`, it evaluates its block
+ again. This continues until the expression finally returns `false`.
+---
+
+{% codeExample 'while' %}
+@use "sass:math";
+
+/// Divides `$value` by `$ratio` until it's below `$base`.
+@function scale-below($value, $base, $ratio: 1.618) {
+ @while $value > $base {
+ $value: math.div($value, $ratio);
+ }
+ @return $value;
+}
+
+$normal-font-size: 16px;
+sup {
+ font-size: scale-below(20px, 16px);
+}
+===
+@use "sass:math"
+
+/// Divides `$value` by `$ratio` until it's below `$base`.
+@function scale-below($value, $base, $ratio: 1.618)
+ @while $value > $base
+ $value: math.div($value, $ratio)
+ @return $value
+
+
+
+$normal-font-size: 16px
+sup
+ font-size: scale-below(20px, 16px)
+===
+sup {
+ font-size: 12.36094px;
+}
+{% endcodeExample %}
+
+{% headsUp %}
+Although `@while` is necessary for a few particularly complex stylesheets,
+you're usually better of using either [`@each`][] or [`@for`][] if either of
+them will work. They're clearer for the reader, and often faster to compile as
+well.
+
+[`@each`]: /documentation/at-rules/control/each
+[`@for`]: /documentation/at-rules/control/for
+{% endheadsUp %}
+
+{% render 'documentation/snippets/truthiness-and-falsiness' %}
diff --git a/source/documentation/at-rules/css.liquid b/source/documentation/at-rules/css.liquid
new file mode 100644
index 000000000..11518c5d2
--- /dev/null
+++ b/source/documentation/at-rules/css.liquid
@@ -0,0 +1,227 @@
+---
+title: CSS At-Rules
+table_of_contents: true
+---
+
+{% # Arguments are (in order): `dart`, `libsass`, `node`, `ruby`, optional feature name, additional details within %}
+{% compatibility '1.15.0', false, null, false, "Name Interpolation" %}
+LibSass, Ruby Sass, and older versions of Dart Sass don't support
+[interpolation][] in at-rule names. They do support interpolation in values.
+
+[interpolation]: /documentation/interpolation
+{% endcompatibility %}
+
+{% markdown %}
+Sass supports all the at-rules that are part of CSS proper. To stay flexible and
+forwards-compatible with future versions of CSS, Sass has general support that
+covers almost all at-rules by default. A CSS at-rule is written
+`@ `, `@ { ... }`, or `@ { ... }`. The name
+must be an identifier, and the value (if one exists) can be pretty much
+anything. Both the name and the value can contain [interpolation][].
+
+[interpolation]: /documentation/interpolation
+{% endmarkdown %}
+
+{% codeExample 'css' %}
+@namespace svg url(http://www.w3.org/2000/svg);
+
+@font-face {
+ font-family: "Open Sans";
+ src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
+}
+
+@counter-style thumbs {
+ system: cyclic;
+ symbols: "\1F44D";
+}
+===
+@namespace svg url(http://www.w3.org/2000/svg)
+
+@font-face
+ font-family: "Open Sans"
+ src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2")
+
+@counter-style thumbs
+ system: cyclic
+ symbols: "\1F44D"
+{% endcodeExample %}
+
+{% markdown %}
+If a CSS at-rule is nested within a style rule, the two automatically swap
+positions so that the at-rule is at the top level of the CSS output and the
+style rule is within it. This makes it easy to add conditional styling without
+having to rewrite the style rule's selector.
+{% endmarkdown %}
+
+{% codeExample 'nested-css-at-rule' %}
+.print-only {
+ display: none;
+
+ @media print { display: block; }
+}
+===
+.print-only
+ display: none
+
+ @media print
+ display: block
+{% endcodeExample %}
+
+{{ '## `@media`' | markdown }}
+
+{% # Arguments are (in order): `dart`, `libsass`, `node`, `ruby`, optional feature name, additional details within %}
+{% compatibility '1.11.0', false, null, '3.7.0', 'Range Syntax', false %}
+{% markdown %}
+LibSass and older versions of Dart Sass and Ruby Sass don't support media
+queries with features written in a [range context][]. They do support other
+standard media queries.
+
+[range context]: https://www.w3.org/TR/mediaqueries-4/#mq-range-context
+{% endmarkdown %}
+
+{% codeExample 'range-syntax', false %}
+@media (width <= 700px) {
+ body {
+ background: green;
+ }
+}
+===
+@media (width <= 700px)
+ body
+ background: green
+===
+@media (width <= 700px) {
+ body {
+ background: green;
+ }
+}
+{% endcodeExample %}
+{% endcompatibility %}
+
+{% markdown %}
+The [`@media` rule][] does all of the above and more. In addition to allowing
+interpolation, it allows [SassScript expressions][] to be used directly in the
+[feature queries][].
+
+[`@media` rule]: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
+[SassScript expressions]: /documentation/syntax/structure#expressions
+[feature queries]: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#Targeting_media_features
+{% endmarkdown %}
+
+{% codeExample 'media-rule' %}
+$layout-breakpoint-small: 960px;
+
+@media (min-width: $layout-breakpoint-small) {
+ .hide-extra-small {
+ display: none;
+ }
+}
+===
+$layout-breakpoint-small: 960px
+
+@media (min-width: $layout-breakpoint-small)
+ .hide-extra-small
+ display: none
+{% endcodeExample %}
+
+{% markdown %}
+When possible, Sass will also merge media queries that are nested within one
+another to make it easier to support browsers that don't yet natively support
+nested `@media` rules.
+{% endmarkdown %}
+
+{% codeExample 'merge-media-queries' %}
+@media (hover: hover) {
+ .button:hover {
+ border: 2px solid black;
+
+ @media (color) {
+ border-color: #036;
+ }
+ }
+}
+===
+@media (hover: hover)
+ .button:hover
+ border: 2px solid black
+
+ @media (color)
+ border-color: #036
+{% endcodeExample %}
+
+{% markdown %}
+## `@supports`
+
+The [`@supports` rule][] also allows [SassScript expressions][] to be used in
+the declaration queries.
+
+[SassScript expressions]: /documentation/syntax/structure#expressions
+[`@supports` rule]: https://developer.mozilla.org/en-US/docs/Web/CSS/@supports
+{% endmarkdown %}
+
+{% codeExample 'support-at-rule' %}
+@mixin sticky-position {
+ position: fixed;
+ @supports (position: sticky) {
+ position: sticky;
+ }
+}
+
+.banner {
+ @include sticky-position;
+}
+===
+@mixin sticky-position
+ position: fixed
+ @supports (position: sticky)
+ position: sticky
+
+
+
+.banner
+ @include sticky-position
+{% endcodeExample %}
+
+{% markdown %}
+## `@keyframes`
+
+The [`@keyframes` rule][] works just like a general at-rule, except that its
+child rules must be valid keyframe rules (`%`, `from`, or `to`) rather
+than normal selectors.
+
+[`@keyframes` rule]: https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes
+{% endmarkdown %}
+
+{% codeExample 'keyframes' %}
+@keyframes slide-in {
+ from {
+ margin-left: 100%;
+ width: 300%;
+ }
+
+ 70% {
+ margin-left: 90%;
+ width: 150%;
+ }
+
+ to {
+ margin-left: 0%;
+ width: 100%;
+ }
+}
+===
+@keyframes slide-in
+ from
+ margin-left: 100%
+ width: 300%
+
+
+ 70%
+ margin-left: 90%
+ width: 150%
+
+
+ to
+ margin-left: 0%
+ width: 100%
+{% endcodeExample %}
diff --git a/source/documentation/at-rules/debug.liquid b/source/documentation/at-rules/debug.liquid
new file mode 100644
index 000000000..55f592fe7
--- /dev/null
+++ b/source/documentation/at-rules/debug.liquid
@@ -0,0 +1,43 @@
+---
+title: "@debug"
+introduction: >
+ Sometimes it’s useful to see the value of a
+ [variable](/documentation/variables) or
+ [expression](/documentation/syntax/structure#expressions) while you’re
+ developing your stylesheet. That’s what the `@debug` rule is for: it’s written
+ `@debug `, and it prints the value of that expression, along with
+ the filename and line number.
+---
+
+{% codeExample 'debug', false %}
+@mixin inset-divider-offset($offset, $padding) {
+ $divider-offset: (2 * $padding) + $offset;
+ @debug "divider offset: #{$divider-offset}";
+
+ margin-left: $divider-offset;
+ width: calc(100% - #{$divider-offset});
+}
+===
+@mixin inset-divider-offset($offset, $padding)
+ $divider-offset: (2 * $padding) + $offset
+ @debug "divider offset: #{$divider-offset}"
+
+ margin-left: $divider-offset
+ width: calc(100% - #{$divider-offset})
+{% endcodeExample %}
+
+{% markdown %}
+The exact format of the debug message varies from implementation to
+implementation. This is what it looks like in Dart Sass:
+
+```
+test.scss:3 Debug: divider offset: 132px
+```
+{% endmarkdown %}
+
+{% funFact %}
+You can pass any value to `@debug`, not just a string! It prints the same
+representation of that value as the [`meta.inspect()` function][].
+
+[`meta.inspect()` function]: /documentation/modules/meta#inspect
+{% endfunFact %}
diff --git a/source/documentation/at-rules/error.liquid b/source/documentation/at-rules/error.liquid
new file mode 100644
index 000000000..28bc0cc8d
--- /dev/null
+++ b/source/documentation/at-rules/error.liquid
@@ -0,0 +1,80 @@
+---
+title: "@error"
+introduction: >
+ When writing [mixins](/documentation/at-rules/mixin) and
+ [functions](/documentation/at-rules/function) that take arguments, you usually
+ want to ensure that those arguments have the types and formats your API
+ expects. If they aren't, the user needs to be notified and your mixin/function
+ needs to stop running.
+---
+
+{% markdown %}
+Sass makes this easy with the `@error` rule, which is written
+`@error `. It prints the value of the [expression][] (usually a
+string) along with a stack trace indicating how the current mixin or function
+was called. Once the error is printed, Sass stops compiling the stylesheet and
+tells whatever system is running it that an error occurred.
+
+[expression]: /documentation/syntax/structure#expressions
+{% endmarkdown %}
+
+{% codeExample 'error', false %}
+@mixin reflexive-position($property, $value) {
+ @if $property != left and $property != right {
+ @error "Property #{$property} must be either left or right.";
+ }
+
+ $left-value: if($property == right, initial, $value);
+ $right-value: if($property == right, $value, initial);
+
+ left: $left-value;
+ right: $right-value;
+ [dir=rtl] & {
+ left: $right-value;
+ right: $left-value;
+ }
+}
+
+.sidebar {
+ @include reflexive-position(top, 12px);
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ // Error: Property top must be either left or right.
+}
+===
+@mixin reflexive-position($property, $value)
+ @if $property != left and $property != right
+ @error "Property #{$property} must be either left or right."
+
+
+ $left-value: if($property == right, initial, $value)
+ $right-value: if($property == right, $value, initial)
+
+ left: $left-value
+ right: $right-value
+ [dir=rtl] &
+ left: $right-value
+ right: $left-value
+
+
+
+.sidebar
+ @include reflexive-position(top, 12px)
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ // Error: Property top must be either left or right.
+{% endcodeExample %}
+
+{% markdown %}
+The exact format of the error and stack trace varies from implementation to
+implementation, and can also depend on your build system. This is what it looks
+like in Dart Sass when run from the command line:
+
+```
+Error: "Property top must be either left or right."
+ ╷
+3 │ @error "Property #{$property} must be either left or right.";
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ ╵
+ example.scss 3:5 reflexive-position()
+ example.scss 19:3 root stylesheet
+```
+{% endmarkdown %}
diff --git a/source/documentation/at-rules/warn.liquid b/source/documentation/at-rules/warn.liquid
new file mode 100644
index 000000000..8426e216b
--- /dev/null
+++ b/source/documentation/at-rules/warn.liquid
@@ -0,0 +1,67 @@
+---
+title: "@warn"
+introduction: >
+ When writing [mixins](/documentation/at-rules/mixin) and
+ [functions](/documentation/at-rules/function), you may want to discourage
+ users from passing certain arguments or certain values. They may be passing
+ legacy arguments that are now deprecated, or they may be calling your API in a
+ way that’s not quite optimal.
+---
+
+{% markdown %}
+The `@warn` rule is designed just for that. It's written `@warn `
+and it prints the value of the [expression][] (usually a string) for the user,
+along with a stack trace indicating how the current mixin or function was
+called. Unlike the [`@error` rule][], though, it doesn't stop Sass entirely.
+
+[expression]: /documentation/syntax/structure#expressions
+[`@error` rule]: /documentation/at-rules/error
+{% endmarkdown %}
+
+{% codeExample 'warn' %}
+$known-prefixes: webkit, moz, ms, o;
+
+@mixin prefix($property, $value, $prefixes) {
+ @each $prefix in $prefixes {
+ @if not index($known-prefixes, $prefix) {
+ @warn "Unknown prefix #{$prefix}.";
+ }
+
+ -#{$prefix}-#{$property}: $value;
+ }
+ #{$property}: $value;
+}
+
+.tilt {
+ // Oops, we typo'd "webkit" as "wekbit"!
+ @include prefix(transform, rotate(15deg), wekbit ms);
+}
+===
+$known-prefixes: webkit, moz, ms, o
+
+@mixin prefix($property, $value, $prefixes)
+ @each $prefix in $prefixes
+ @if not index($known-prefixes, $prefix)
+ @warn "Unknown prefix #{$prefix}."
+
+
+ -#{$prefix}-#{$property}: $value
+
+ #{$property}: $value
+
+
+.tilt
+ // Oops, we typo'd "webkit" as "wekbit"!
+ @include prefix(transform, rotate(15deg), wekbit ms)
+{% endcodeExample %}
+
+{% markdown %}
+The exact format of the warning and stack trace varies from implementation to
+implementation. This is what it looks like in Dart Sass:
+
+```
+Warning: Unknown prefix wekbit.
+ example.scss 6:7 prefix()
+ example.scss 16:3 root stylesheet
+```
+{% endmarkdown %}
diff --git a/source/documentation/snippets/truthiness-and-falsiness.liquid b/source/documentation/snippets/truthiness-and-falsiness.liquid
new file mode 100644
index 000000000..0b20338f4
--- /dev/null
+++ b/source/documentation/snippets/truthiness-and-falsiness.liquid
@@ -0,0 +1,23 @@
+{% markdown %}
+## Truthiness and Falsiness
+
+Anywhere `true` or `false` are allowed, you can use other values as well. The
+values `false` and [`null`][] are *falsey*, which means Sass considers them to
+indicate falsehood and cause conditions to fail. Every other value is considered
+*truthy*, so Sass considers them to work like `true` and cause conditions to
+succeed.
+
+[`null`]: /documentation/values/null
+
+For example, if you want to check if a string contains a space, you can just
+write `string.index($string, " ")`. The [`string.index()` function][] returns
+`null` if the string isn't found and a number otherwise.
+
+[`string.index()` function]: /documentation/modules/string#index
+{% endmarkdown %}
+
+{% headsUp %}
+Some languages consider more values falsey than just `false` and `null`. Sass
+isn't one of those languages! Empty strings, empty lists, and the number `0` are
+all truthy in Sass.
+{% endheadsUp %}
diff --git a/source/helpers/components/compatibility.ts b/source/helpers/components/compatibility.ts
index bc1a41f20..5c74f2b9d 100644
--- a/source/helpers/components/compatibility.ts
+++ b/source/helpers/components/compatibility.ts
@@ -28,6 +28,7 @@ export const compatibility = async (
node: string | boolean | null = null,
ruby: string | boolean | null = null,
feature: string | null = null,
+ useMarkdown = true,
) =>
liquidEngine.renderFile('compatibility', {
details,
@@ -36,6 +37,7 @@ export const compatibility = async (
node,
ruby,
feature,
+ useMarkdown,
});
/**