<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Scss on Lessons learned</title><link>https://eghuro.cz/tags/scss/</link><description>Recent content in Scss on Lessons learned</description><generator>Hugo</generator><language>en-us</language><copyright>© Alexandr Mansurov · Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)</copyright><lastBuildDate>Sat, 24 Oct 2026 14:58:00 +0200</lastBuildDate><atom:link href="https://eghuro.cz/tags/scss/index.xml" rel="self" type="application/rss+xml"/><item><title>Moodle 5.3 Classic theme serves an empty stylesheet: 'Undefined variable $mds-spacing-md'</title><link>https://eghuro.cz/posts/moodle-53-classic-theme-empty-stylesheet/</link><pubDate>Sat, 24 Oct 2026 14:58:00 +0200</pubDate><guid>https://eghuro.cz/posts/moodle-53-classic-theme-empty-stylesheet/</guid><description>&lt;p&gt;After an upgrade to Moodle 5.3, every page on the Classic theme lost its layout. The theme&amp;rsquo;s stylesheet came back like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ curl -s -o /dev/null -w &amp;#39;%{http_code} %{size_download}\n&amp;#39; \&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#39;https://moodle.example.com/theme/styles_debug.php?theme=classic&amp;amp;type=scss&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;200 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s a 200 with an empty body, and nothing in the logs. With debugging forced to developer level, the SCSS compiler said:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error while compiling SCSS: Undefined variable $mds-spacing-md: /opt/moodle/public/theme/classic/scss/../../boost/scss/moodle/course.scss on line 132, at column 17&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Classic imports Boost&amp;rsquo;s SCSS, and in 5.3 that SCSS uses the new design system&amp;rsquo;s tokens. Classic never imports them. One &lt;code&gt;@import&lt;/code&gt; fixes it.&lt;/p&gt;&#10;&#10;&lt;h2 id="the-setup"&gt;The setup &lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Moodle 5.3.0, PHP 8.4.27, in a Docker image of our own, upgraded from 5.0&lt;/li&gt;&#10;&lt;li&gt;The site&amp;rsquo;s theme is Classic. It left core in 5.3, so the image installs it from &lt;a&#10; href="https://github.com/moodlehq/moodle-theme_classic"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;moodlehq/moodle-theme_classic&lt;/a&gt;&#10; (at commit &lt;code&gt;363a179&lt;/code&gt;, the only one when I checked)&lt;/li&gt;&#10;&lt;li&gt;Theme designer mode was on, so pages loaded the theme through &lt;code&gt;styles_debug.php&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;I rehearsed the upgrade on a copy of the production database. Every URL I tried returned 200, and &lt;code&gt;admin/cli/checks.php&lt;/code&gt; reported all checks OK. On production, the pages had no layout.&lt;/p&gt;&#10;&#10;&lt;h2 id="two-things-that-looked-like-the-cause"&gt;Two things that looked like the cause &lt;/h2&gt;&#10;&lt;p&gt;The browser console had two errors:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Error] Failed to load resource: the server responded with a status of 404 (Not Found) (client.bundle.js.map, line 0)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Error] Failed to load resource: the server responded with a status of 404 (Not Found) (react.bundle.mjs.map, line 0)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those are source maps. The browser asks for them only for its developer tools, and Moodle doesn&amp;rsquo;t ship them. They don&amp;rsquo;t affect the layout.&lt;/p&gt;&#10;&lt;p&gt;The import map had a few 404s as well, for entries like &lt;code&gt;/core/esm/&amp;lt;rev&amp;gt;/bootstrap/&lt;/code&gt;. Those entries end in a slash because they map a whole prefix, so the bare URL has nothing to serve. The modules under them loaded.&lt;/p&gt;&#10;&lt;p&gt;The problem showed up when I fetched every stylesheet the front page links to and printed its size:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -s https://moodle.example.com/ -o index.html&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -oE &lt;span class="s1"&gt;&amp;#39;&amp;lt;link[^&amp;gt;]+stylesheet[^&amp;gt;]*&amp;gt;&amp;#39;&lt;/span&gt; index.html &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -oE &lt;span class="s1"&gt;&amp;#39;href=&amp;#34;[^&amp;#34;]+&amp;#34;&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;&amp;#39;s/href=&amp;#34;//; s/&amp;#34;$//; s/&amp;amp;amp;/\&amp;amp;/g&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r u&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -s &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; wc -c&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;done&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The plugin stylesheets had a few kilobytes each. The theme&amp;rsquo;s own, &lt;code&gt;type=scss&lt;/code&gt;, had zero bytes.&lt;/p&gt;&#10;&#10;&lt;h2 id="getting-the-real-error"&gt;Getting the real error &lt;/h2&gt;&#10;&lt;p&gt;Moodle catches the compiler&amp;rsquo;s exception and passes the message to &lt;code&gt;debugging()&lt;/code&gt; at developer level, so at the usual debug setting you get nothing at all. You don&amp;rsquo;t have to change the site&amp;rsquo;s settings to see it. Compile the theme from the CLI with debugging switched on for that one process (run it as the web server&amp;rsquo;s user and adjust the path to &lt;code&gt;config.php&lt;/code&gt;):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u www-data php -r &lt;span class="s1"&gt;&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;define(&amp;#34;CLI_SCRIPT&amp;#34;, 1);&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;require &amp;#34;/opt/moodle/config.php&amp;#34;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;$CFG-&amp;gt;debug = E_ALL;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;$CFG-&amp;gt;debugdisplay = 1;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;$t = \core\output\theme_config::load(&amp;#34;classic&amp;#34;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;$m = new ReflectionMethod($t, &amp;#34;get_css_content_from_scss&amp;#34;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;$c = $m-&amp;gt;invoke($t, false);&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;var_dump(is_string($c) ? strlen($c) : $c);&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;get_css_content_from_scss&lt;/code&gt; isn&amp;rsquo;t public, hence the reflection. It returns &lt;code&gt;false&lt;/code&gt; when the compile fails, and the error comes with the import chain:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;++ Error while compiling SCSS: Undefined variable $mds-spacing-md: /opt/moodle/public/theme/classic/scss/../../boost/scss/moodle/course.scss on line 132, at column 17&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Call Stack:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#0 import /opt/moodle/public/theme/classic/scss/../../boost/scss/moodle/course.scss /opt/moodle/public/theme/classic/scss/../../boost/scss/moodle.scss on line 13&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#1 import /opt/moodle/public/theme/classic/scss/../../boost/scss/moodle.scss /opt/moodle/public/theme/classic/scss/moodle.scss on line 2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#2 import /opt/moodle/public/theme/classic/scss/moodle.scss (unknown file) on line 127 ++&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;h2 id="why"&gt;Why &lt;/h2&gt;&#10;&lt;p&gt;Boost&amp;rsquo;s default preset, &lt;code&gt;theme/boost/scss/preset/default.scss&lt;/code&gt;, starts with:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scss" data-lang="scss"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Import Moodle Design System tokens.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Must be imported before Bootstrap so that the design system tokens can be used to override Bootstrap variables.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;design-system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;design-system.scss&lt;/code&gt; pulls in the &lt;code&gt;$mds-*&lt;/code&gt; tokens. Classic&amp;rsquo;s presets import &lt;code&gt;fontawesome&lt;/code&gt;, &lt;code&gt;bootstrap&lt;/code&gt; and &lt;code&gt;moodle&lt;/code&gt;, but not &lt;code&gt;design-system&lt;/code&gt;, and Classic&amp;rsquo;s &lt;code&gt;moodle.scss&lt;/code&gt; imports Boost&amp;rsquo;s. So Boost&amp;rsquo;s &lt;code&gt;course.scss&lt;/code&gt; uses &lt;code&gt;$mds-spacing-md&lt;/code&gt;, and nothing has defined it.&lt;/p&gt;&#10;&#10;&lt;h2 id="the-fix"&gt;The fix &lt;/h2&gt;&#10;&lt;p&gt;Add the import to the top of &lt;code&gt;theme/classic/scss/classic/pre.scss&lt;/code&gt;. Classic&amp;rsquo;s &lt;code&gt;lib.php&lt;/code&gt; puts that file in front of the preset, so this covers both presets:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;pre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/moodle/public/theme/classic/scss/classic/pre.scss&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;@import &amp;#34;../../boost/scss/design-system&amp;#34;;\n\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; cat &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$pre&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &amp;gt; /tmp/pre.scss&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mv /tmp/pre.scss &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$pre&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u www-data php /opt/moodle/admin/cli/purge_caches.php --theme&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The path resolves from &lt;code&gt;theme/classic/scss/&lt;/code&gt;, as the call stack above shows. After the purge, the same CLI compile returned 1,218,070 characters instead of &lt;code&gt;false&lt;/code&gt;, and &lt;code&gt;styles_debug.php&lt;/code&gt; sent 1,557,703 bytes.&lt;/p&gt;&#10;&lt;p&gt;If Classic is part of your build, as here, apply the patch in the build. A fix made inside a running container is gone the next time the container is recreated.&lt;/p&gt;&#10;&lt;p&gt;Once the layout was back, I turned theme designer mode off (&lt;code&gt;admin/cli/cfg.php --name=themedesignermode --set=0&lt;/code&gt;). It recompiles the theme on every page view: here, about 0.8 s and 112 stylesheet requests per page.&lt;/p&gt;&#10;&#10;&lt;h2 id="takeaway"&gt;Takeaway &lt;/h2&gt;&#10;&lt;p&gt;A Moodle theme that fails to compile still answers 200, just with no CSS, so upgrade checks that look only at status codes pass right over it.&lt;/p&gt;&#10;&lt;p&gt;Before your next Moodle upgrade, add the theme&amp;rsquo;s compiled CSS size to your checklist: anything near zero is a failed compile.&lt;/p&gt;&#10;</description></item></channel></rss>