diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf9a3b79b..ec1c24b51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 - Fixed a crash when a `SelectionList` had a prompt wider than itself https://github.com/Textualize/textual/issues/2900
 - Fixed a bug where `Click` events were bubbling up from `Switch` widgets https://github.com/Textualize/textual/issues/2366
+- Fixed a crash when using empty CSS variables https://github.com/Textualize/textual/issues/1849
 
 ## [0.30.0] - 2023-07-17
 
diff --git a/src/textual/css/parse.py b/src/textual/css/parse.py
index 13aeea488..1b31e8b66 100644
--- a/src/textual/css/parse.py
+++ b/src/textual/css/parse.py
@@ -269,6 +269,7 @@ def substitute_references(
             break
         if token.name == "variable_name":
             variable_name = token.value[1:-1]  # Trim the $ and the :, i.e. "$x:" -> "x"
+            variable_tokens = variables.setdefault(variable_name, [])
             yield token
 
             while True:
@@ -284,7 +285,7 @@ def substitute_references(
                 if not token:
                     break
                 elif token.name == "whitespace":
-                    variables.setdefault(variable_name, []).append(token)
+                    variable_tokens.append(token)
                     yield token
                 elif token.name == "variable_value_end":
                     yield token
@@ -293,7 +294,6 @@ def substitute_references(
                 elif token.name == "variable_ref":
                     ref_name = token.value[1:]
                     if ref_name in variables:
-                        variable_tokens = variables.setdefault(variable_name, [])
                         reference_tokens = variables[ref_name]
                         variable_tokens.extend(reference_tokens)
                         ref_location = token.location
@@ -307,7 +307,7 @@ def substitute_references(
                     else:
                         _unresolved(ref_name, variables.keys(), token)
                 else:
-                    variables.setdefault(variable_name, []).append(token)
+                    variable_tokens.append(token)
                     yield token
                 token = next(iter_tokens, None)
         elif token.name == "variable_ref":
diff --git a/src/textual/events.py b/src/textual/events.py
index 4e2523535..3f6015dfb 100644
--- a/src/textual/events.py
+++ b/src/textual/events.py
@@ -4,7 +4,7 @@ Builtin events sent by Textual.
 
 Events may be marked as "Bubbles" and "Verbose".
 See the [events guide](/guide/events/#bubbling) for an explanation of bubbling.
-Verbose events are excluded from the textual console, unless you explicit request them with the `-v` switch as follows:
+Verbose events are excluded from the textual console, unless you explicitly request them with the `-v` switch as follows:
 
 ```
 textual console -v

diff --git a/tests/css/test_parse.py b/tests/css/test_parse.py
index fea7b3dad..cb6e6aad0 100644
--- a/tests/css/test_parse.py
+++ b/tests/css/test_parse.py
@@ -220,6 +220,22 @@ class TestVariableReferenceSubstitution:
         with pytest.raises(UnresolvedVariableError):
             list(substitute_references(tokenize(css, "")))
 
+    def test_empty_variable(self):
+        css = "$x:\n* { background:$x; }"
+        result = list(substitute_references(tokenize(css, "")))
+        assert [(t.name, t.value) for t in result] == [
+            ("variable_name", "$x:"),
+            ("variable_value_end", "\n"),
+            ("selector_start_universal", "*"),
+            ("whitespace", " "),
+            ("declaration_set_start", "{"),
+            ("whitespace", " "),
+            ("declaration_name", "background:"),
+            ("declaration_end", ";"),
+            ("whitespace", " "),
+            ("declaration_set_end", "}"),
+        ]
+
     def test_transitive_reference(self):
         css = "$x: 1\n$y: $x\n.thing { border: $y }"
         assert list(substitute_references(tokenize(css, ""))) == [

Allow for empty CSS variable definitions
Consider the following CSS snippets:

 1.
```sass
$x: 
* {
    background: red;
}
```

2.
```sass
$x:
* {
    background: $x;
}
```

3.
```sass
* {
    background: red;
}
$x:
```

All three should work with the following behaviour:
 - an empty variable definition is allowed; and
 - setting a style to an empty variable should unset that style.

For example, 2. should unset the background of all widgets.

As of now, 2. and 3. can't be parsed.
2. raises an error regarding an undefined variable and 3. raises an `AttributeError` inside `substitute_references` in `parse.py`.
tests/css/test_parse.py::TestVariableReferenceSubstitution::test_empty_variable
tests/css/test_parse.py::TestVariableReferenceSubstitution::test_simple_reference tests/css/test_parse.py::TestVariableReferenceSubstitution::test_simple_reference_no_whitespace tests/css/test_parse.py::TestVariableReferenceSubstitution::test_undefined_variable tests/css/test_parse.py::TestVariableReferenceSubstitution::test_transitive_reference tests/css/test_parse.py::TestVariableReferenceSubstitution::test_multi_value_variable tests/css/test_parse.py::TestVariableReferenceSubstitution::test_variable_used_inside_property_value tests/css/test_parse.py::TestVariableReferenceSubstitution::test_variable_definition_eof tests/css/test_parse.py::TestVariableReferenceSubstitution::test_variable_reference_whitespace_trimming tests/css/test_parse.py::TestParseLayout::test_valid_layout_name tests/css/test_parse.py::TestParseLayout::test_invalid_layout_name tests/css/test_parse.py::TestParseText::test_foreground tests/css/test_parse.py::TestParseText::test_background tests/css/test_parse.py::TestParseColor::test_rgb_and_hsl[rgb(1,255,50)-result0] tests/css/test_parse.py::TestParseColor::test_rgb_and_hsl[rgb( tests/css/test_parse.py::TestParseColor::test_rgb_and_hsl[rgba( tests/css/test_parse.py::TestParseColor::test_rgb_and_hsl[hsl( tests/css/test_parse.py::TestParseColor::test_rgb_and_hsl[hsl(180,50%,50%)-result5] tests/css/test_parse.py::TestParseColor::test_rgb_and_hsl[hsla(180,50%,50%,0.25)-result6] tests/css/test_parse.py::TestParseColor::test_rgb_and_hsl[hsla( tests/css/test_parse.py::TestParseOffset::test_composite_rule[-5.5%-parsed_x0--30%-parsed_y0] tests/css/test_parse.py::TestParseOffset::test_composite_rule[5%-parsed_x1-40%-parsed_y1] tests/css/test_parse.py::TestParseOffset::test_composite_rule[10-parsed_x2-40-parsed_y2] tests/css/test_parse.py::TestParseOffset::test_separate_rules[-5.5%-parsed_x0--30%-parsed_y0] tests/css/test_parse.py::TestParseOffset::test_separate_rules[5%-parsed_x1-40%-parsed_y1] tests/css/test_parse.py::TestParseOffset::test_separate_rules[-10-parsed_x2-40-parsed_y2] tests/css/test_parse.py::TestParseOverflow::test_multiple_enum tests/css/test_parse.py::TestParseTransition::test_various_duration_formats[5.57s-5.57] tests/css/test_parse.py::TestParseTransition::test_various_duration_formats[0.5s-0.5] tests/css/test_parse.py::TestParseTransition::test_various_duration_formats[1200ms-1.2] tests/css/test_parse.py::TestParseTransition::test_various_duration_formats[0.5ms-0.0005] tests/css/test_parse.py::TestParseTransition::test_various_duration_formats[20-20.0] tests/css/test_parse.py::TestParseTransition::test_various_duration_formats[0.1-0.1] tests/css/test_parse.py::TestParseTransition::test_no_delay_specified tests/css/test_parse.py::TestParseTransition::test_unknown_easing_function tests/css/test_parse.py::TestParseOpacity::test_opacity_to_styles[-0.2-0.0] tests/css/test_parse.py::TestParseOpacity::test_opacity_to_styles[0.4-0.4] tests/css/test_parse.py::TestParseOpacity::test_opacity_to_styles[1.3-1.0] tests/css/test_parse.py::TestParseOpacity::test_opacity_to_styles[-20%-0.0] tests/css/test_parse.py::TestParseOpacity::test_opacity_to_styles[25%-0.25] tests/css/test_parse.py::TestParseOpacity::test_opacity_to_styles[128%-1.0] tests/css/test_parse.py::TestParseOpacity::test_opacity_invalid_value tests/css/test_parse.py::TestParseMargin::test_margin_partial tests/css/test_parse.py::TestParsePadding::test_padding_partial tests/css/test_parse.py::TestParseTextAlign::test_text_align[left] tests/css/test_parse.py::TestParseTextAlign::test_text_align[start] tests/css/test_parse.py::TestParseTextAlign::test_text_align[center] tests/css/test_parse.py::TestParseTextAlign::test_text_align[right] tests/css/test_parse.py::TestParseTextAlign::test_text_align[end] tests/css/test_parse.py::TestParseTextAlign::test_text_align[justify] tests/css/test_parse.py::TestParseTextAlign::test_text_align_invalid tests/css/test_parse.py::TestParseTextAlign::test_text_align_empty_uses_default tests/css/test_parse.py::TestTypeNames::test_type_no_number tests/css/test_parse.py::TestTypeNames::test_type_with_number tests/css/test_parse.py::TestTypeNames::test_type_starts_with_number tests/css/test_parse.py::TestTypeNames::test_combined_type_no_number tests/css/test_parse.py::TestTypeNames::test_combined_type_with_number tests/css/test_parse.py::TestTypeNames::test_combined_type_starts_with_number tests/css/test_parse.py::test_parse_bad_psuedo_selector