Migrate from sassc to modern CSS compilation - #527
Conversation
- Replace deprecated `sassc-rails` (LibSass) with `dartsass-sprockets`. - Update `application.scss` to use modern Sass module syntax (`@use "sass:color"`). - Rename DataTables CSS files to SCSS partials to ensure they are inlined during compilation, fixing a production 404 bug. - Add `config/initializers/dartsass.rb` to silence Bootstrap `@import` deprecation warnings. - Fix SVG data URI encoding by adding `%` to `$escaped-characters` to handle Dart Sass color serialization. - Remove LibSass-specific `unquote()` workarounds for CSS variables. Co-authored-by: Claude Code <noreply@anthropic.com>
The app carried hand-maintained copies of dataTables.bootstrap5.css and responsive.bootstrap5.css under app/assets/stylesheets. They had already drifted from the JavaScript pinned in config/importmap.rb, and one of them carried a libsass workaround (unquote() around rgb(var(--x))) that Dart Sass does not need. Delete both and @import the stylesheets straight out of node_modules instead, with the two package directories added to the Sass load path. The npm versions are pinned exactly to the versions pinned in config/importmap.rb -- 2.3.1 and 3.0.4 -- so the CSS and the JavaScript stay in lockstep and both move in a single, reviewable bump. This makes node_modules a prerequisite for compiling CSS, so: - the Docker build stage installs nodejs/npm and runs npm ci --omit=dev. It runs after COPY . . so that npm ci (which wipes node_modules first) also clears any node_modules that rode along in the build context; the runtime image still carries no Node. - the rspec, cucumber and a11y workflows set up Node and npm ci - bin/setup runs npm install Realigning to 2.3.1 changes the compiled CSS by 14 tokens: DataTables added .dt-left/.dt-justify header alignment rules and tightened the sort-arrow gutter from 20px to .25rem. Verified against origin/main with the same app code: rspec 619/0, the a11y suite 40/0 with all 40 screenshots pixel-compared (17 byte-identical, the rest differing only in generated timestamps and the slightly narrower sort-arrow gutter), cucumber unchanged at the same 7 pre-existing failures. Production precompiles clean, boots, and serves one stylesheet with DataTables inlined and no stray @import. Co-Authored-By: Claude <noreply@anthropic.com>
e1889f9 to
9388a3a
Compare
Follow-on to the dartsass-sprockets migration. Same Dart Sass compiler,
different plumbing: Propshaft digests and serves what is on disk and does no
transformation, so Dart Sass writes app/assets/builds/application.css and
Propshaft takes it from there.
sprockets-rails -> propshaft
dartsass-sprockets -> dartsass-rails
What goes away: app/assets/config/manifest.js, the config.assets.precompile
list, and five Sprockets-only environment settings (assets.debug, .digest,
.quiet, .compile, .css_compressor).
Four things needed handling:
1. Font Awesome webfonts. font-awesome-sass emits font-path() only when
Sprockets' Sass helpers are loaded, and otherwise falls back to
"#{$fa-font-path}/#{$file}" -- i.e. ../webfonts/..., which 404s. Set
$fa-font-path to the gem's logical asset path and let Propshaft rewrite
the url() to a digested one.
2. Gem Sass sources leaking into public/assets. Propshaft has a single load
path and serves everything on it, and the bootstrap and font-awesome-sass
gems put their Sass sources there so application.scss can @import them --
110 .scss partials were being digested into public/assets. They cannot
simply be excluded, because dartsass-rails derives its --load-path list
from config.assets.paths and Propshaft's excluded_paths removes entries
from that same array. Pass the two directories to Dart Sass explicitly and
then take them off Propshaft's path.
3. The CSS is a build artifact now. A checkout without it raises
Propshaft::MissingAssetError rather than rendering unstyled, so bin/setup
builds it and the three CI workflows that run rspec/cucumber directly get
a Build CSS step. Procfile.dev gains a css process, so `make dev` keeps it
fresh.
4. Dev output. dartsass-rails defaults to --style=compressed --no-source-map
in every environment, where sassc-rails only compressed outside
development. Build expanded with source maps in development.
Verified: rspec 584/0; the a11y suite 40/0 with all 40 screenshots
pixel-compared against the Sprockets build (20 byte-identical, the rest
differing only where generated timestamps change column widths); cucumber
unchanged at the same 5 pre-existing failures. Production and development
boot with CSS, webfonts and all 27 local importmap assets returning 200. The
compiled CSS is token-for-token identical to the Sprockets output apart from
minifier cosmetics (nested calc(), leading zeros).
Co-Authored-By: Claude <noreply@anthropic.com>
|
Rebased onto Conflict resolution. 149c632 fixed the same DataTables 404 by moving both stylesheets to Second commit — load DataTables CSS from npm. Rather than keep vendored copies at all, This makes
Realigning 2.2.x → 2.3.1 changes the compiled CSS by 14 tokens: DataTables added Verified against Not verified: the Docker image build — no Docker available in this environment. The Dockerfile change is the one thing worth a careful look. |
…spike Migrate the asset pipeline from Sprockets to Propshaft
General Info
Changes
Replaces the deprecated
sassc-rails(LibSass) gem withdartsass-sprockets, resolving the Gemfile TODO. Dart Sass is the only maintained Sass implementation; everything else about the pipeline (Sprockets,stylesheet_link_tag,manifest.js, gem-provided SCSS) is unchanged.Key changes:
Gemfile— swapsassc-railsfordartsass-sprockets ~> 3.2. (sasscitself remains as a transitive dependency offont-awesome-sassandsassc-embedded, but no longer compiles anything.)config/initializers/dartsass.rb(new) — silences Bootstrap 5.3's@importdeprecation warnings (quiet_depsfor gem stylesheets,silence_deprecations: ['import']for our own). Can be removed when Bootstrap 6 ships.application.scss— adds@use "sass:color"and updatesmix()calls tocolor.mix()(the modern Sass module API). Adds%→%25to$escaped-charactersso Dart Sass's fractional color serialization doesn't drop bare%into SVG data URIs..cssto_*.scsspartials and re-imported without the.cssextension. Previously,@import "….css"was passed through as a plain CSS@import url(...)rather than being inlined — meaning both DataTables stylesheets 404'd in production since they were never precompiled. Inlining fixes this. Theunquote("rgb(var(--x))")LibSass workarounds are also removed, since Dart Sass handlesrgb(var(…))natively.Testing
rspec: 584 examples, 0 failuresrspec --tag a11y: 40 examples, 0 failures (all pages × light/dark × role, axe-audited)cucumber: same 5 pre-existing failures, no new failuresapplication.csswith DataTables rules inlined, no stray@importremainingVisual Changes
No styling changes — only test-data noise (timestamps) differs between old and new screenshots.
Documentation
No documentation changes required.
Checklist
Superconductor Ticket Implementation | App Preview | Guided Review