Skip to content

Fix DataTables CSS and sync_poller import 404ing in production - #531

Merged
cycomachead merged 1 commit into
mainfrom
cycomachead/fix-datatables-asset-loading
Aug 5, 2026
Merged

Fix DataTables CSS and sync_poller import 404ing in production#531
cycomachead merged 1 commit into
mainfrom
cycomachead/fix-datatables-asset-loading

Conversation

@superconductor-for-github

Copy link
Copy Markdown

Split out of #404 (admin dashboard UI), which had picked up these asset fixes incidentally. This PR carries only the asset-loading fixes so #404 can stay scoped to the admin nav.

Two things are broken in production builds

1. DataTables CSS never loads

application.scss imported the vendored stylesheets as:

@import "datatables/dataTables.bootstrap5.css";

Sass treats a .css extension as a passthrough, not an inline. It does not paste the file in; it emits a literal @import url(...) into the compiled output. Confirmed by inspecting the precompiled stylesheet:

/* public/assets/application-<digest>.css, line 5 */
@import url(datatables/dataTables.bootstrap5.css);@import url(datatables/responsive.bootstrap5.css);:root,...

That URL resolves relative to the stylesheet, i.e. /assets/datatables/dataTables.bootstrap5.css. Nothing precompiles that path — app/assets/config/manifest.js only links application.css, and config/initializers/assets.rb doesn't add it. Production sets config.assets.compile = false, so there is no runtime fallback: it 404s and every DataTable renders unstyled.

2. sync_poller never loads

assignment_controller.js and enrollments_controller.js imported it relatively:

import { pollUntilDone } from "./sync_poller";

Under importmap the importing module is served digested (/assets/controllers/assignment_controller-<digest>.js), so ./sync_poller resolves to /assets/controllers/sync_poller.js — undigested, and never precompiled under that name. Only sync_poller-<digest>.js exists on disk.

The fixes

  1. Serve the two DataTables stylesheets from public/ via plain <link> tags, and delete the now-unused copies under app/assets/stylesheets/datatables/ so exactly one copy remains.
  2. Import as controllers/sync_poller, which goes through the existing pin_all_from "app/javascript/controllers", under: "controllers" pin and resolves to the digested asset.

Why public/ and not a proper pipeline fix

The obvious fix — drop the .css extension so Sass actually inlines the file — does not compile under sassc-rails:

SassC::SyntaxError: Error: Function rgb is missing argument $green.
>>   background: rgb(var(--dt-row-selected));

libsass parses rgb(var(--x)) as a Sass function call. The old app/assets copy worked around this by wrapping each such value in unquote("..."), but that is a Sass-only construct and would be invalid if the file were served directly. The public/ copies are therefore raw upstream, with no unquote() workaround — correct for plain-CSS serving.

A comment in application.html.erb records all of this so nobody "fixes" it back into the pipeline.

Known trade-offs (follow-ups, not blockers)

  • No fingerprinting. These two <link> tags get no digest, so a future DataTables upgrade will serve stale bytes from browser cache until the max-age expires. The real fix is moving off sassc-rails (dartsass-sprockets handles rgb(var(--x)) natively) — already noted as blocked on Bootstrap 5.3's deprecated @import usage in the Gemfile.
  • Loaded on every page, including ones with no table.
  • Version drift. The CSS should track the datatables.net-bs5 (2.3.1) and datatables.net-responsive-bs5 (3.0.4) pins in config/importmap.rb. I could not verify the vendored copies match those exact versions — the CDN is blocked from my sandbox by network policy. Worth a manual check.

Verification

  • rails assets:precompile succeeds, and the compiled application.css no longer contains any stray @import url(...) (0 occurrences, was 2).
  • Both <link> targets exist under public/datatables/, and sync_poller-<digest>.js is emitted.
  • cucumber features/assignments.feature features/enrollments.feature features/navigation.feature: 5 passed, 5 failed — byte-identical to the origin/main baseline (same 5 scenario IDs). No regression. All 5 are @javascript scenarios that cannot pass in my sandbox: application.js does import "@rails/ujs", which is pinned to https://ga.jspm.io/..., and that CDN is blocked by network policy, so the ES module graph fails to resolve in headless Chrome and no Stimulus controller runs. These need a CI run to actually validate — that is the environment where the real behavioral fix should show up.
  • rubocop: clean.

Two asset paths were broken in production builds:

1. DataTables CSS. application.scss did `@import "datatables/...css"`, but
   Sass treats a `.css` extension as a passthrough: it does not inline the
   file, it emits a literal `@import url(datatables/dataTables.bootstrap5.css)`
   into the compiled stylesheet. That URL resolves to
   /assets/datatables/dataTables.bootstrap5.css, which is not in the
   precompile list, and production sets config.assets.compile = false -- so
   it 404s and DataTables renders unstyled. Serve both files from public/
   via plain <link> tags instead, and delete the now-unused copies under
   app/assets/stylesheets/datatables/ so there is exactly one copy.

   Dropping the .css extension so Sass inlines it was tried and does not
   compile under sassc-rails (`Function rgb is missing argument $green`),
   because the vendored file uses rgb(var(--custom-prop)). The public/ copies
   are therefore raw upstream, with no unquote() Sass workaround.

2. sync_poller. assignment_controller.js and enrollments_controller.js
   imported it as "./sync_poller", which resolves relative to the digested
   module URL and yields /assets/controllers/sync_poller.js -- again never
   precompiled under that name. Import it as "controllers/sync_poller" so it
   goes through the `pin_all_from ... under: "controllers"` importmap pin and
   resolves to the digested asset.

Verified: assets:precompile succeeds and the compiled application.css no
longer contains a stray `@import url(...)`.
@cycomachead
cycomachead merged commit 83c0a99 into main Aug 5, 2026
13 checks passed
@cycomachead
cycomachead deleted the cycomachead/fix-datatables-asset-loading branch August 5, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant