Mobile-First SCSS Architecture with Dart Sass in Docker
Our SCSS is split into three folders and compiled by tools inside Docker, so developers do not need Sass installed on the host. The source and generated files still live on a bind-mounted project directory. Here's the se
Our SCSS is split into three folders and compiled by tools inside Docker, so developers do not need Sass installed on the host. The source and generated files still live on a bind-mounted project directory. Here's the setup and why it matters.
The Directory Structure
assets/scss/site/
βββ common/ β variables, breakpoints, shared partials
βββ mobile/ β base styles and small-screen adjustments
β βββ _catalog_list.scss
β βββ _catalog_item.scss
β βββ _account.scss
β βββ _main.scss
βββ desktop/ β wider-screen overrides
βββ _catalog_list.scss
βββ _catalog_item.scss
βββ _main.scss
The mobile folder contains the base appearance. It still has a few targeted media and accessibility queries; βmobile-firstβ does not have to mean βno media queries.β
The desktop folder primarily contains wider-screen overrides. If a component does not change at the desktop breakpoint, it usually needs no matching desktop partial.
Why This Matters
The separation makes the cascade and ownership clear: base rules live in one place, and desktop files describe the delta. We compile separate outputs and apply the desktop stylesheet conditionally:
<link rel="stylesheet" href="/storage/static/css/site/main_catalog_list_mobile.css">
<link rel="stylesheet"
href="/storage/static/css/site/main_catalog_list_desktop.css"
media="(min-width: 641px) and (min-height: 500px)">
The media attribute controls when the desktop CSS applies. Browsers may fetch a nonmatching stylesheet at lower priority, but it does not guarantee that the file will not be downloaded. Verify transfer behavior with browser tooling instead of treating file separation as an automatic bandwidth saving.
Building Inside Docker
We never run sass on the host machine. The Docker container has Dart Sass installed:
# docker-compose.yml (simplified)
site:
build:
context: .
dockerfile: .docker/site/site.dockerfile
command: ["make", "-f", "/Makefile", "dev"]
volumes:
- ./src:/app
- ./Makefile:/Makefile
The development target builds assets once, then runs Air and Sass concurrently:
dev: build-css-dev build-js-dev
$(MAKE) -j 2 run-air watch-css
run-air:
cd $(SRC_DIR) && air -c .air.site.toml
watch-css:
sass --watch --poll $(SCSS_INPUTS) --style=expanded --no-source-map
We use --poll because filesystem event delivery from bind mounts varies across host and Docker combinations. Polling is not universally required, so test both modes in your own environment.
Compiled Output
storage/static/css/site/
βββ main_catalog_list_mobile.css
βββ main_catalog_list_desktop.css
βββ main_catalog_item_mobile.css
βββ main_catalog_item_desktop.css
βββ main_account_mobile.css
βββ main_account_desktop.css
βββ bundle_mobile.css
βββ bundle_desktop.css
Our static web server serves the compiled files directly, without routing them through the Go application.
The Gotcha: Air Doesn't Watch SCSS
Air's config explicitly excludes assets/ and storage/:
[build]
exclude_dir = ["assets", "storage", "tmp"]
This is intentional. SCSS changes are handled by sass --watch, which runs in parallel. If Air watched SCSS files too, it would rebuild the Go binary on every CSS edit β pointless and slow.
JavaScript has no watcher in this setup. After editing an entry file, we run:
docker compose exec -T site make -f /Makefile build-js-dev
This is a deliberate project trade-off, not a requirement of Docker or Sass.
What We'd Change
If starting over, we would evaluate native CSS plus Lightning CSS, or keep Sass and add a separate post-processing step. Lightning CSS can transform, prefix, bundle, and minify CSS, but it is not a drop-in compiler for SCSS syntax.
Dart Sass remains a good fit for the current partials, and keeping the toolchain in Docker gives every developer the same compiler version.
This article is based on lessons from building Towami, a multi-country marketplace and white-label storefront platform. Follow for more practical notes on Go, HTMX, infrastructure, and SaaS.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.