Engineering Journal
Ginexys
Ginexys

Deeplinks Errorfix

2026-05-30

Modal renders unstyled in a tool that does not load portfolio.css

TLDR

ginexys-modals.css references 20 CSS custom properties defined in portfolio.css. PDF processor's standalone build never loaded portfolio.css, so the modal opened with no background, no rounded corners, no typography. The fix was extracting just the modal-needed vars to a 2KB tokens file instead of loading the full 48KB portfolio stylesheet which would have collided with PDF processor's own design tokens.

Symptom

User opens /tools/pdf-processor/ standalone. Clicks a Pro feature. Modal appears as raw form fields stacked on the default browser background. No dark overlay. No card boundary. No gold accent on the submit button.

DevTools inspector showed every var(--surface-raised) and var(--gold-base) resolving to nothing. Computed styles showed background: ; and border-radius: ;.

Same modal renders correctly inside the OS shell and on TAFNE and Schema editor. The other three surfaces all load portfolio.css.

Root cause

ginexys-modals.css uses 20 CSS custom properties:

.gx-modal {
  background: var(--surface-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  color: var(--text-primary);
  font-family: var(--font-body);
  / and 15 more /
}

These are defined in portfolio.css:

:root {
  --surface-raised: #1b2835;
  --border-default: #1e3348;
  --radius-lg: 10px;
  --text-primary: #d8eaf8;
  --font-body: "DM Mono", monospace;
  / and 15 more /
}

PDF processor's src/styles.css has its own design tokens with different names (--bg, --text, --header-h). It never loads portfolio.css because portfolio.css would override body { background: var(--surface-base); overflow: hidden } and other global rules that collide with PDF processor's flexbox column layout.

So the modal CSS loaded, referenced variables that did not exist in PDF processor's :root, and CSS fell through to default values. CSS variables that resolve to nothing render as empty strings on background, color, border-radius. No errors, no warnings, just empty rules.

Fix

Extract only the modal-needed vars to a separate file:

/ assets/css/ginexys-modals-tokens.css /
:root {
  --surface-raised:        #1b2835;
  --surface-overlay:       #192230;
  --border-default:        #1e3348;
  --text-primary:          #d8eaf8;
  --gold-base:             #00b4d8;
  --radius-lg:             10px;
  --font-body:             "DM Mono", monospace;
  / the 20 vars ginexys-modals.css actually references /
}
[data-theme="light"] {
  --surface-raised:  #f2f8ff;
  --border-default:  #42586c;
  / light overrides /
}

Link it before ginexys-modals.css in pdf-processor:

<link rel="stylesheet" href="src/styles.css">
<link rel="stylesheet" href="/assets/css/ginexys-modals-tokens.css">
<link rel="stylesheet" href="/assets/css/ginexys-modals.css">

2KB total. No global selectors. No collision with pdf-processor's own body, header, or layout styles. Modal renders correctly.

Guard

If you add a new CSS variable to a ginexys-modals.css selector, add it to ginexys-modals-tokens.css too. The grep that found the original list was:

grep -oE "var\(--[a-z0-9-]+" assets/css/ginexys-modals.css | sort -u

20 results. Run it before shipping any modal CSS change.

Lesson

CSS variables fail silently. Missing fonts trigger fallback chains and you notice. Missing background colors render transparent and you might notice. Missing custom properties render as empty strings and the layout just looks slightly off.

When you split CSS into a shared library plus per-app overrides, document which :root vars the library expects. If your library is ginexys-modals.css, it needs those vars defined somewhere upstream. If you ship it to a context that doesn't have those vars, the rules silently no-op.

The right pattern is what we ended up with: extract the contract into its own tokens file. The library imports become a two-step include. Per-app design systems can override individual tokens without inheriting the full stylesheet. No silent failures.

If your modal looks "weird" and you cannot pinpoint why, the first DevTools check is computed values of var(--…) references in the modal selectors. If they show empty strings instead of resolved values, your design tokens never reached this context. Search the page's CSS for the :root block. If it is not there, add it.

Read this post in the full Engineering Journal →