Skip to content

feat: add surface, font and dark mode controls - #446

Open
uniqueg wants to merge 17 commits into
devfrom
feat/theming-controls
Open

uniqueg wants to merge 17 commits into
devfrom
feat/theming-controls

Conversation

@uniqueg

@uniqueg uniqueg commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Description

Extends the native theming controls of all component packages, and fixes theming bugs found by trying to restyle the WES components using only those controls.

New controls

  • surface attribute (opt-in). Components stay transparent by default. With surface, a component paints its own background and text color from --ecc-background and --ecc-foreground, inset by the new --ecc-surface-padding (default 1rem) and rounded by --ecc-radius. A component can then use a dark (or any other) theme on any page.
  • --ecc-font-sans and --ecc-font-mono tokens. Without them, components still inherit the page font. Tailwind's font-mono (run IDs etc.) and the code editor follow --ecc-font-mono.
  • Token-driven code editor theme. It replaces the hard-coded cloud9_day / cloud9_night Ace themes. Background, gutter, syntax colors, selection, fold arrows and font all derive from the tokens, so the editor matches custom and dark themes.

Fixes

  • The dark attribute did nothing in Firefox. :host([dark]) shared a selector list with :host-context(), which Firefox does not support, so Firefox dropped the whole rule. Dark defaults are now set as inherited private --_ecc-default-* properties in a separate :host([dark]) rule. Components nested inside other components inherit them without needing :host-context(). Explicit --ecc-* tokens still win.
  • Error banners were near-invisible in light mode. They used text-destructive-foreground (near-white) on a 10% destructive tint; they now use text-destructive. To keep error text readable in dark mode, the dark --ecc-destructive default is now oklch(0.704 0.191 22.216) (shadcn's current value). The dark --ecc-destructive-foreground is now oklch(0.205 0 0), and the destructive button uses that token instead of hard-coded text-white, so solid destructive badges and buttons stay readable.
  • The design package's tests could not run at all. The shared test runner config did not pass the package tsconfig to esbuild, so Lit's decorators crashed on import.

Docs: apps/documentation/pages/docs/customization.mdx covers all of the above.

Behavior changes to be aware of

  • ecc-utils-design-code no longer has the public methods isDarkMode(), updateEditorTheme() and setupThemeObserver(); nothing in the repo calls them. The editor now goes dark through tokens like every other component, not by reading <html>/<body> classes in JavaScript.
    • As a result, <html class="dark"> in Firefox or Safari no longer darkens only the editor. It never darkened the rest of the components in those browsers, because :host-context() is Chromium-only. The editor is now consistent with the rest.
  • Error text color now comes from --ecc-destructive. Apps that copied the old dark default (oklch(0.396 …)) into their own theme get dark red error text on dark backgrounds; the docs include a note on this.

How it was verified

  • npm run build (all 9 turbo tasks, including the docs site): passes.
  • npm run lint: passes, 0 errors. Warning counts are unchanged except design (11 → 13, any casts around the global ace object, matching existing code).
  • Design package tests, packages/ecc-utils-design/src/global.test.ts, 15 tests:
    • Chromium 15/15.
    • Firefox 14 passed, 1 skipped. The skip is intentional: the ancestor .dark test is feature-detected because Firefox lacks :host-context().
    • With the old global.ts swapped back in, the Firefox dark attribute tests fail, as do the surface and font tests, confirming they cover the fixes.
  • WES package tests, packages/ecc-client-ga4gh-wes/src/theming.test.ts: --ecc-font-sans on runs, run and run-create (catches the style-order bug from review), and run-create's error color. Chromium 4/4, Firefox 4/4. Package tests run on fixed ports (design 8000, WES 8001), because two packages now run in parallel and could otherwise both pick 8000 (EADDRINUSE, reproduced in review).
  • WebKit could not run locally (the host lacks libevent-2.1-7t64 and libavif16), but it runs in CI, see below.
  • CI (run 40003004065; rerun on f6b6ee2 in run 40023922794: Test checks pass on Node 18.x and 20.x):
  • Visual checks in headless Chrome:
    • WES runs, run details and run creation side by side in a conservative light theme and a dark surface theme.
    • Error banners in light and dark.
    • Code editor in light and dark, including fold arrows.

Declined review findings

  • "Default font changes from Tailwind sans to the page font." Not a change: on main, every package's tailwind.css already has an unlayered :host { font-family: inherit }, which beats Tailwind's layered preflight. The "inherits the page font" test also passes against the old styles.
  • Make global.ts copies drift-proof (shared module or identity check). The 8 copies are byte-identical, which is the existing repo convention. Moving them into one shared module is a worthwhile follow-up refactor but outside this PR's scope.
  • Add a changeset (changeset-bot comment). Since July 2025 (v2.0.0-alpha.28), releases are tag-driven: pushing a v* tag runs publish.yml, which sets all package versions, commits bump: v… to main and publishes to npm. The changesets flow (version.yml, "Version Packages" PRs) was last used in December 2024 (Version Packages #383). Adding a changeset would revive that parallel flow and open a competing version PR.
  • Force display: block on surface hosts. That would override the layout of inline design components such as badges and buttons. The docs instead state that surface is meant for top-level, block-level (service) components.
  • Drop the docs note about the old dark destructive default. Kept, because oklch(0.396 …) shipped in published releases (e.g. 2.0.0-alpha.48).

Unrelated fixes

  • ci: drop test scripts from packages without tests (85d8bdc). Seven packages ran wtr without any test files. wtr then exits with "Could not find any test files", which failed the turbo test run, and with it the Test checks job of every PR since at least February, before the packages with tests could finish. Without a test script, turbo skips these packages; add the script back together with a package's first test.
  • test: honor package tsconfig in shared wtr config (769ebf9). Without it, the design package's tests could not even be imported.

Open questions

  • What makes the Vercel preview deployment fail? It fails within 2 seconds, before any build step, so the cause is probably in the Vercel project settings. Someone with access to the elixir-cloud-aai Vercel team needs to check npx vercel inspect dpl_6DyaMFtU1Mi7mmyv1gUd5BD7qBq1 --logs or the project's Node.js version setting.
  • run-create's success message uses hsl(var(--success)), but no --success token exists, so the message is never colored. Should we add an --ecc-success token, or reuse an existing one (e.g. --ecc-chart-2)? Left unchanged here.

Checklist

  • My code follows the contributing guidelines of this project.
  • I am aware that all my commits will be squashed into a single commit, using the PR title as the commit message.
  • I have performed a self-review of my own code.
  • I have commented my code in hard-to-understand areas.
  • I have updated the user-facing documentation to describe any new or changed behavior.
  • My changes generate no new warnings. (Two lint warnings for any casts, see above.)
  • I have added tests that prove my fix is effective or that my feature works.
  • I have not reduced the existing code coverage.

🤖 Generated with Claude Code

Summary by Sourcery

Extend native component theming with surface, typography, and token-driven editor controls while fixing cross-browser dark mode and error contrast issues.

New Features:

  • Add opt-in surface painting and configurable sans and monospace font tokens across component packages.
  • Make the code editor theme follow ECC design tokens, including custom and dark themes.

Bug Fixes:

  • Restore component-level dark mode behavior in Firefox and improve inheritance of dark defaults for nested components.
  • Improve error and destructive button contrast across light and dark themes.
  • Fix design package test imports by honoring package TypeScript configuration and prevent parallel test port conflicts.

Enhancements:

  • Standardize theming token resolution and Tailwind typography integration across native components.
  • Remove the code editor's JavaScript-based theme detection and obsolete public theme methods.
  • Update WES component test configuration and add coverage for font and error theming.

Documentation:

  • Document surface painting, typography tokens, dark-mode behavior, code editor theming, and destructive color guidance.

Tests:

  • Add cross-browser design theming tests covering surfaces, fonts, dark mode, token precedence, and editor colors.
  • Add WES tests covering configurable fonts and error colors.

Chores:

  • Remove failing test scripts from packages without test files.

uniqueg and others added 13 commits October 23, 2026 19:52
The shared web-test-runner config compiled tests without the package
tsconfig, so esbuild emitted standard decorators and Lit's legacy
decorators crashed on import ("Cannot read properties of undefined
(reading 'constructor')"). No design package test could run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Extend the shared GlobalStyles of all packages:

- `surface` attribute (opt-in): the component paints its own background
  and text color from --ecc-background / --ecc-foreground, so a theme
  (e.g. dark) works independently of the host page.
- --ecc-font-sans and --ecc-font-mono tokens; without them, the page
  font is still inherited. Tailwind's font-mono now follows the token.
- Dark defaults are now set as inherited private --_ecc-default-*
  properties in a separate :host([dark]) rule. Previously, :host([dark])
  shared a selector list with :host-context(), which Firefox does not
  support, so the whole rule was dropped and the dark attribute did
  nothing in Firefox. Nested components now inherit dark defaults
  without needing :host-context().
- Dark --destructive defaults to a lighter red (shadcn's current value)
  so it can be used as error text on dark backgrounds; dark
  --destructive-foreground becomes near-white for solid badges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the hard-coded cloud9_day/cloud9_night Ace themes with an "ecc"
theme whose CSS reads the design tokens (background, foreground, muted
gutter, primary keys/keywords, chart-derived strings and numbers,
--ecc-font-mono). The editor now follows custom themes and dark mode in
CSS, so the JavaScript dark-mode detection is removed: it only watched
the class of <html>/<body>, and its this.matches(":host-context(...)")
checks never match from outside a shadow root.

Add tests for the surface attribute, font tokens, dark mode (incl.
nested components) and the editor theme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Error banners used text-destructive-foreground on a 10% destructive
tint. That token is meant for text on a solid destructive background
and defaults to near-white in light mode, so error messages were
practically invisible. Use text-destructive instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Document the typography tokens, dark mode behavior (including the
Firefox limitation for ancestor-based detection), the opt-in surface
attribute, how the code editor maps tokens, and the new dark
destructive defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A painted surface without inset put content flush against its edge.
Components with the surface attribute now get padding from the new
--ecc-surface-padding token (default 1rem) and rounded corners from
--ecc-radius.

Also extend tests (surface padding, light-mode control, .dark ancestor
where :host-context() is supported, editor gutter token) and correct
the docs on which tokens the code editor uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use a foreground tint for the active line instead of --accent, which
produced a heavy colored band in themes with a saturated accent. Name
the syntax color helper and its blend ratio, and explain the static
isDark flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hosts styled width: 100% overflowed their container by twice the
surface padding. Use border-box sizing on surfaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
White text on the lighter dark-mode --destructive was about 2.9:1;
use a dark --destructive-foreground (about 6:1). Document that only
Chromium supports ancestor-based dark mode (Safari lacks
:host-context() too) and that error text now uses --ecc-destructive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ace switches its fold arrow images via the ace_dark class, which the
token-driven theme cannot set, so arrows were dark on dark gutters.
Draw them as masks filled with --muted-foreground instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Assert that fold arrows use the token-colored mask instead of Ace's
images, explain why the width test uses the code editor, and tighten
the ace-theme comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The destructive button hard-coded text-white, which is about 2.9:1 on
the lighter dark-mode --destructive (its dark: variant cannot match
across the shadow boundary). Use --destructive-foreground like the
badge; the light default stays near-white.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep a destructive tint for invalid fold widgets, which the token
fold-arrow rule otherwise overrode. Wait for the gutter with waitUntil
instead of a fixed timeout, and document that surface is meant for
block-level components.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: f6b6ee2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
elixir-cloud-components Error Error Oct 23, 2026 9:03pm UTC

@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR centralizes component theming around inherited ECC tokens, adding opt-in surface painting and configurable fonts, fixing cross-browser dark-mode propagation and destructive contrast, and replacing the code editor’s JavaScript-driven Ace themes with a CSS token-driven theme. It documents and tests the new behavior across Chromium and Firefox while repairing shared test-runner configuration and parallel package test execution.

Sequence diagram for token-driven code editor initialization

sequenceDiagram
    participant Component as EccUtilsDesignCode
    participant Ace as Ace editor
    participant CSS as ECC token CSS
    Component->>Ace: initializeAceEditor()
    Component->>Ace: setTheme(ACE_THEME)
    Ace->>CSS: Apply ace-ecc styles
    CSS-->>Ace: Resolve background, syntax colors, selection, fold arrows and font from tokens
Loading

Flow diagram for cross-browser dark mode resolution

flowchart TD
    Start["Component renders"] --> Explicit{dark attribute on component?}
    Explicit -->|Yes| Defaults["Set inherited --_ecc-default-* dark properties"]
    Explicit -->|No| Ancestor{dark ancestor selector supported?}
    Ancestor -->|Yes| Defaults
    Ancestor -->|No| Light["Keep light defaults"]
    Defaults --> Tokens["Resolve --ecc-* tokens"]
    Light --> Tokens
    Tokens --> Override["Explicit --ecc-* tokens take precedence"]
Loading

Flow diagram for opt-in surface painting

flowchart LR
    Component["Component host"] --> Default["No surface attribute: transparent and page-integrated"]
    Component --> OptIn{surface attribute}
    OptIn --> Painted["Paint --ecc-background and --ecc-foreground"]
    Painted --> Layout["Apply --ecc-surface-padding, --ecc-radius and box-sizing"]
Loading

File-Level Changes

Change Details Files
Adds opt-in surface painting and typography controls across component packages.
  • Paints surface hosts with token-driven background, foreground, padding, radius, and border-box sizing.
  • Adds --ecc-font-sans and --ecc-font-mono, wiring Tailwind monospace utilities to the latter.
  • Documents the new surface, typography, and dark-mode behavior and compatibility constraints.
apps/documentation/pages/docs/customization.mdx
packages/ecc-client-elixir-cloud-registry/src/global.ts
packages/ecc-client-elixir-cloud-registry/src/tailwind.css
packages/ecc-client-elixir-drs-filer/src/global.ts
packages/ecc-client-elixir-drs-filer/src/tailwind.css
packages/ecc-client-elixir-trs-filer/src/global.ts
packages/ecc-client-elixir-trs-filer/src/tailwind.css
packages/ecc-client-ga4gh-drs/src/global.ts
packages/ecc-client-ga4gh-drs/src/tailwind.css
packages/ecc-client-ga4gh-service-registry/src/global.ts
packages/ecc-client-ga4gh-service-registry/src/tailwind.css
packages/ecc-client-ga4gh-trs/src/global.ts
packages/ecc-client-ga4gh-trs/src/tailwind.css
packages/ecc-client-ga4gh-wes/src/global.ts
packages/ecc-client-ga4gh-wes/src/tailwind.css
packages/ecc-utils-design/src/global.ts
packages/ecc-utils-design/src/tailwind.css
Reworks dark-mode token resolution to function reliably on component-local attributes and nested shadow trees.
  • Introduces inherited private dark-default properties, preserving explicit public token overrides.
  • Separates :host([dark]) from unsupported :host-context() selectors for Firefox/Safari behavior.
  • Updates dark destructive defaults for readable error text and button content.
packages/ecc-client-elixir-cloud-registry/src/global.ts
packages/ecc-client-elixir-drs-filer/src/global.ts
packages/ecc-client-elixir-trs-filer/src/global.ts
packages/ecc-client-ga4gh-drs/src/global.ts
packages/ecc-client-ga4gh-service-registry/src/global.ts
packages/ecc-client-ga4gh-trs/src/global.ts
packages/ecc-client-ga4gh-wes/src/global.ts
packages/ecc-utils-design/src/global.ts
Replaces Ace's fixed light/dark themes with a CSS token-driven editor theme.
  • Defines a custom Ace theme whose editor, gutter, syntax, selection, cursor, fold markers, and font use ECC-derived variables.
  • Removes JavaScript document-class observation and the public editor theme helper methods.
  • Adds token-controlled fold-arrow masks and loads the custom theme during editor initialization.
packages/ecc-utils-design/src/components/code/ace-theme.ts
packages/ecc-utils-design/src/components/code/code.ts
Improves destructive/error styling throughout service and design components.
  • Uses the destructive token for tinted error-banner text instead of destructive-foreground.
  • Uses destructive-foreground for destructive button text rather than hard-coded white.
  • Corrects WES run-create error styling to use the resolved CSS token.
packages/ecc-client-elixir-drs-filer/src/components/object-create/object-create.ts
packages/ecc-client-ga4gh-drs/src/components/object/object.ts
packages/ecc-client-ga4gh-drs/src/components/objects-list/objects.ts
packages/ecc-client-ga4gh-service-registry/src/components/service/service.ts
packages/ecc-client-ga4gh-service-registry/src/components/services/services.ts
packages/ecc-client-ga4gh-trs/src/components/tool/tool.ts
packages/ecc-client-ga4gh-trs/src/components/tools/tools.ts
packages/ecc-client-ga4gh-wes/src/components/run/run.ts
packages/ecc-client-ga4gh-wes/src/components/runs/runs.ts
packages/ecc-client-ga4gh-wes/src/components/run-create/run-create.ts
packages/ecc-utils-design/src/components/button/button.ts
Adds cross-browser theming coverage and makes parallel test execution reliable.
  • Adds design tests for surface, typography, dark defaults, token precedence, nested inheritance, and editor styling.
  • Adds WES tests for font propagation and destructive error colors.
  • Passes package tsconfig to esbuild for Lit decorator support and assigns fixed ports to parallel test packages.
  • Removes test scripts from packages without test files and deletes the obsolete WES-specific runner config.
packages/ecc-utils-design/src/global.test.ts
packages/ecc-client-ga4gh-wes/src/theming.test.ts
web-test.runner-config.mjs
packages/ecc-utils-design/package.json
packages/ecc-client-ga4gh-wes/package.json
packages/ecc-client-ga4gh-wes/web-test-runner.config.mjs
packages/ecc-client-elixir-cloud-registry/package.json
packages/ecc-client-elixir-drs-filer/package.json
packages/ecc-client-elixir-trs-filer/package.json
packages/ecc-client-ga4gh-drs/package.json
packages/ecc-client-ga4gh-service-registry/package.json
packages/ecc-client-ga4gh-trs/package.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Seven packages ran wtr with no test files, which exits with "Could not
find any test files" and failed the turbo test run (and so the Test
checks job of every PR) before packages with tests could finish.
Without a test script, turbo skips them. Re-add the script together
with the first test file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@anuragxxd anuragxxd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, thanks! The dark-mode inheritance fix and the CI test-script cleanup both look right.

One bug before merge: --ecc-font-sans has no effect on ecc-client-ga4gh-wes-run-create. run-create.ts:35-37 lists [GlobalStyles, TailwindStyles], so Tailwind's :host { font-family: inherit } (not in a layer, same specificity) overrides it. Every other component uses [TailwindStyles, GlobalStyles]. Swapping the order fixes it. A font test on one client-package component would catch this, since the current tests only cover badge.

uniqueg and others added 3 commits October 23, 2026 22:53
run-create listed GlobalStyles before TailwindStyles, so Tailwind's
unlayered :host { font-family: inherit } overrode --ecc-font-sans.
Use the order every other component uses. Also fix its error message
color: hsl(var(--destructive)) wrapped an oklch() token and was
invalid, so errors were not colored at all.

Add WES theming tests (font token on all three components, run-create
error color) and a test script using the shared Playwright config.

Reported in review of #446.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test:watch still used the per-package wtr config (default Chrome
launcher, development exports, no tsconfig), so watch runs differed
from npm test. Mirror the design package scripts and remove the WES
config. Clarify the theming test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
turbo runs package tests in parallel. With two packages using the
shared wtr config, both could pick port 8000 between portfinder's
check and the bind, failing one run with EADDRINUSE (reproduced in
review). Pin design to 8000 and wes to 8001.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@uniqueg

uniqueg commented Oct 23, 2026

Copy link
Copy Markdown
Member Author

Looks good overall, thanks! The dark-mode inheritance fix and the CI test-script cleanup both look right.

One bug before merge: --ecc-font-sans has no effect on ecc-client-ga4gh-wes-run-create. run-create.ts:35-37 lists [GlobalStyles, TailwindStyles], so Tailwind's :host { font-family: inherit } (not in a layer, same specificity) overrides it. Every other component uses [TailwindStyles, GlobalStyles]. Swapping the order fixes it. A font test on one client-package component would catch this, since the current tests only cover badge.

Thanks, good catch! Fixed in b0b3e04: swapped the order, and also fixed run-create's error color (hsl(var(--destructive)) was invalid with oklch tokens). Added WES tests for --ecc-font-sans on all three components plus the error color. A script check confirmed run-create was the only component with the reversed order. Also pinned per-package test ports in f6b6ee2, since two packages now test in parallel.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="packages/ecc-utils-design/src/components/code/ace-theme.ts" line_range="123" />
<code_context>
+// globalThis rather than window: this module is also evaluated during SSR
+const { ace } = globalThis as any;
+
+ace.define(
+  ACE_THEME,
+  ["require", "exports", "module", "ace/lib/dom"],
</code_context>
<issue_to_address>
**SSR imports fail without Ace**

When the design package is imported during server-side rendering where `ace-builds` does not set `globalThis.ace`, the module-level `ace.define(...)` call runs with `ace` undefined and throws, so importing the design package during SSR fails.

Guard the registration so it runs only when the Ace global is available.

Also at `packages/ecc-utils-design/src/components/code/ace-theme.ts:121-122`.
</issue_to_address>

### Comment 2
<location path="packages/ecc-utils-design/src/components/code/code.ts" line_range="666" />
<code_context>
-    });
-  }
-
-  isDarkMode() {
-    return (
-      document.documentElement.classList.contains("dark") ||
</code_context>
<issue_to_address>
**Public editor methods are missing**

When an external consumer calls a previously public editor method, `EccUtilsDesignCode` no longer exposes `isDarkMode()`, `updateEditorTheme()`, or `setupThemeObserver()`, so TypeScript calls fail or runtime calls throw a `TypeError`, breaking downstream consumers.

Restore the removed public editor methods on `EccUtilsDesignCode`.
</issue_to_address>

### Comment 3
<location path="packages/ecc-utils-design/src/components/button/button.ts" line_range="22" />
<code_context>
   default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
   destructive:
-    "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+    "bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
   outline:
     "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
</code_context>
<issue_to_address>
**Dark button variants stay inactive**

When a button using a `dark:*` variant is rendered in the new attribute-based dark mode, the new dark-token handling does not add a `.dark` ancestor inside the button’s shadow root, but Tailwind’s dark variant matches only `.dark *` (`packages/ecc-utils-design/src/tailwind.css:4`). Thus `dark:bg-destructive/60` and the other `dark:*` button utilities do not apply when dark mode is activated with the documented `dark` attribute or an ancestor theme, so the button keeps its non-dark variant styling.

Make the dark variant match the host’s `dark` attribute and supported dark ancestors across the shadow boundary, or replace these dark-specific utilities with token-driven styles.
</issue_to_address>

### Comment 4
<location path="packages/ecc-utils-design/src/global.test.ts" line_range="190-191" />
<code_context>
+      await waitUntil(() => inShadow(el, ".ace_fold-widget"));
+      const widget = inShadow(el, ".ace_fold-widget");
+      const style = getComputedStyle(widget);
+      expect(style.backgroundImage).to.equal("none");
+      expect(style.backgroundColor).to.equal("rgb(11, 22, 33)");
+    });
+
</code_context>
<issue_to_address>
**Fold arrows can render as solid blocks**

When the fold-arrow mask is removed or stops applying, the fold-widget test checks only that `backgroundImage` is `none` and `backgroundColor` matches the token; both assertions still pass if the mask that shapes the widget into an arrow is missing, so the test passes while users see a colored block instead of an arrow.

Assert that the computed mask is present, in addition to checking the token color and absence of Ace's background image.
</issue_to_address>

Sourcery assessment

Approval pending. 4 findings to address first.

Blocking findings: packages/ecc-utils-design/src/components/code/ace-theme.ts:123, packages/ecc-utils-design/src/components/code/code.ts:666, packages/ecc-utils-design/src/components/button/button.ts:22, packages/ecc-utils-design/src/global.test.ts:191


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

// globalThis rather than window: this module is also evaluated during SSR
const { ace } = globalThis as any;

ace.define(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · SSR imports fail without Ace

When the design package is imported during server-side rendering where ace-builds does not set globalThis.ace, the module-level ace.define(...) call runs with ace undefined and throws, so importing the design package during SSR fails.

Guard the registration so it runs only when the Ace global is available.

Also at packages/ecc-utils-design/src/components/code/ace-theme.ts:121-122.

Prompt for AI agents
In `packages/ecc-utils-design/src/components/code/ace-theme.ts` at line 123:

**SSR imports fail without Ace**

When the design package is imported during server-side rendering where `ace-builds` does not set `globalThis.ace`, the module-level `ace.define(...)` call runs with `ace` undefined and throws, so importing the design package during SSR fails.

Guard the registration so it runs only when the Ace global is available.

Also at `packages/ecc-utils-design/src/components/code/ace-theme.ts:121-122`.

});
}

isDarkMode() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Public editor methods are missing

When an external consumer calls a previously public editor method, EccUtilsDesignCode no longer exposes isDarkMode(), updateEditorTheme(), or setupThemeObserver(), so TypeScript calls fail or runtime calls throw a TypeError, breaking downstream consumers.

Restore the removed public editor methods on EccUtilsDesignCode.

Prompt for AI agents
In `packages/ecc-utils-design/src/components/code/code.ts` at line 666:

**Public editor methods are missing**

When an external consumer calls a previously public editor method, `EccUtilsDesignCode` no longer exposes `isDarkMode()`, `updateEditorTheme()`, or `setupThemeObserver()`, so TypeScript calls fail or runtime calls throw a `TypeError`, breaking downstream consumers.

Restore the removed public editor methods on `EccUtilsDesignCode`.

default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
"bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Dark button variants stay inactive

When a button using a dark:* variant is rendered in the new attribute-based dark mode, the new dark-token handling does not add a .dark ancestor inside the button’s shadow root, but Tailwind’s dark variant matches only .dark * (packages/ecc-utils-design/src/tailwind.css:4). Thus dark:bg-destructive/60 and the other dark:* button utilities do not apply when dark mode is activated with the documented dark attribute or an ancestor theme, so the button keeps its non-dark variant styling.

Make the dark variant match the host’s dark attribute and supported dark ancestors across the shadow boundary, or replace these dark-specific utilities with token-driven styles.

Prompt for AI agents
In `packages/ecc-utils-design/src/components/button/button.ts` at line 22:

**Dark button variants stay inactive**

When a button using a `dark:*` variant is rendered in the new attribute-based dark mode, the new dark-token handling does not add a `.dark` ancestor inside the button’s shadow root, but Tailwind’s dark variant matches only `.dark *` (`packages/ecc-utils-design/src/tailwind.css:4`). Thus `dark:bg-destructive/60` and the other `dark:*` button utilities do not apply when dark mode is activated with the documented `dark` attribute or an ancestor theme, so the button keeps its non-dark variant styling.

Make the dark variant match the host’s `dark` attribute and supported dark ancestors across the shadow boundary, or replace these dark-specific utilities with token-driven styles.

Comment on lines +190 to +191
expect(style.backgroundImage).to.equal("none");
expect(style.backgroundColor).to.equal("rgb(11, 22, 33)");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Fold arrows can render as solid blocks

When the fold-arrow mask is removed or stops applying, the fold-widget test checks only that backgroundImage is none and backgroundColor matches the token; both assertions still pass if the mask that shapes the widget into an arrow is missing, so the test passes while users see a colored block instead of an arrow.

Assert that the computed mask is present, in addition to checking the token color and absence of Ace's background image.

Prompt for AI agents
In `packages/ecc-utils-design/src/global.test.ts` at lines 190-191:

**Fold arrows can render as solid blocks**

When the fold-arrow mask is removed or stops applying, the fold-widget test checks only that `backgroundImage` is `none` and `backgroundColor` matches the token; both assertions still pass if the mask that shapes the widget into an arrow is missing, so the test passes while users see a colored block instead of an arrow.

Assert that the computed mask is present, in addition to checking the token color and absence of Ace's background image.

This branch had an error being deployed

1 failed deployment
Preview — f6b6ee2b Deployed Oct 23, 2026 by vercel[bot]
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.

2 participants