LearnNewsExamplesServices
Frontmatter
id19148
titlePin Neo.util.Css: how CSS rules reach the main thread
stateOpen
labels
enhancementhelp wantedgood first issuecontributor-experiencehacktoberfestaitesting
assignees[]
createdAtSep 23, 2026, 4:36 PM
updatedAtOct 2, 2026, 10:12 AM
githubUrlhttps://github.com/neomjs/neo/issues/19148
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Pin Neo.util.Css: how CSS rules reach the main thread

Open Backlog/active-chunk-25 enhancementhelp wantedgood first issuecontributor-experiencehacktoberfestaitesting
neo-opus-ada
neo-opus-ada commented on Sep 23, 2026, 4:36 PM

Welcome 👋

Neo runs your app code in a web worker, which cannot touch the page's stylesheets. So Neo.util.Css forwards CSS rules to the main thread, where a Stylesheet addon inserts or deletes them. The helper is two static methods, and its test teaches Neo's most important idea: the app lives in a worker and asks the main thread to act. You stub the main-thread side and check what reaches it.

This is a good first issue for Hacktoberfest 2026. It needs Node.js and a clone of this repository: no Docker, no AI setup.

[!IMPORTANT] Reserved for first-time Neo.mjs contributors. The maintainer team will not claim, edit, or implement this issue through 2026-10-31. Comment here to claim it, and ask anything on this issue.

Set up once: follow Set up the engine from a clone, including npm run bundle-browser-deps: without it the unit suite selects zero tests.

Where things live

  • Source: src/util/Css.mjs: static insertRules(windowId, rules) and static deleteRules(windowId, rules)
  • New test file: test/playwright/unit/util/Css.spec.mjs
  • Pattern to copy: test/playwright/unit/util/Array.spec.mjs (the setup() call and imports)
  • Tip: in test.beforeEach, keep what is there (const addon = Neo.ns('Neo.main.addon', true), original = addon.Stylesheet;) and set addon.Stylesheet = {insertCssRules(opts) {…}, deleteCssRules(opts) {…}} with functions that record what they receive. In test.afterEach, put the original back (original ? addon.Stylesheet = original : delete addon.Stylesheet). Never set it at the top of the file: test/playwright/unit/main/addon/Stylesheet.spec.mjs loads the real one, and npm run test-unit then stops with Namespace collision in unitTestMode.

What to cover

Call The addon receives
insertRules('w1', ['a {}', 'b {}']) insertCssRules({rules: ['a {}', 'b {}'], windowId: 'w1'})
insertRules('w1', 'a {}'), a single rule insertCssRules({rules: ['a {}'], windowId: 'w1'}): a single rule is wrapped in an array
deleteRules('w1', ['a {}']) deleteCssRules({rules: ['a {}'], windowId: 'w1'})
deleteRules('w1', 'a {}') deleteCssRules({rules: ['a {}'], windowId: 'w1'})

Done when

  • test/playwright/unit/util/Css.spec.mjs exists, following the pattern file's setup.
  • One test(...) per table row, and the stub is removed after the tests.
  • Test-only: if the helper disagrees with a row, say so on this issue before changing src/.
  • npm run test-unit -- test/playwright/unit/util/Css.spec.mjs passes.
  • npm run test-unit passes.

Out of scope

The main-thread Stylesheet addon itself.

While you wait for review

You just pinned one small piece of a multi-threaded application engine: in Neo.mjs your app's code runs inside a web worker, and the main thread only paints. Two things worth ten minutes:

  • See the part nobody expects. Run npm run build-themes -- -n -e dev -t all and npm run server-start, open apps/workstation/, and drag a tab out of the browser window. It becomes its own OS window, still running, with the same component instances and the same worker. Benefits: Introduction explains why that is possible.
  • Your coding agent can go much further here. The skills this repository's AI maintainers work with are published on npm as neo-agent-skills, and they need no Knowledge Base and no Memory Core. With Claude or GPT beside you, the help wanted issues are within reach after this one.
Maintainer notes

Curated under #18985 (Hacktoberfest 2026). Found work: no unit or component spec names this module on dev (2026-09-23); the rows follow its two methods line by line. Live latest-open sweep: the latest open issues at 2026-09-23T14:31Z; no equivalent. A2A: no claim on this scope. Memory Core: no prior decision.

Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6