Summary
packages/ui/src/styles/default.css's @theme inline block (lines 65-73) declares:
--tracking-normal: var(--tracking-normal);
--shadow-2xl: var(--shadow-2xl);
--shadow-xl: var(--shadow-xl);
--shadow-lg: var(--shadow-lg);
--shadow-md: var(--shadow-md);
--shadow: var(--shadow);
--shadow-sm: var(--shadow-sm);
--shadow-xs: var(--shadow-xs);
--shadow-2xs: var(--shadow-2xs);
Each of these nine declarations defines a custom property as a reference to itself. Per the CSS
custom-properties spec, a property that (directly or indirectly) references itself is invalid at
computed-value time, and the browser falls back to the property's initial value — which for
box-shadow is none and for letter-spacing is normal. Tailwind then compiles shadow-xs,
shadow-sm, shadow, shadow-md, shadow-lg, shadow-xl, shadow-2xl and tracking-tighter
through tracking-widest all in terms of these same nine circular variables, so every one of
those utility classes renders with no visible effect, including on button.tsx's own
shadow-xs (used by every button variant).
Control that isolates this to these nine variables specifically, not @theme inline or Tailwind
compilation in general: every other variable resolved the same way in the same file (e.g.
--color-primary: var(--primary), where --primary is independently declared in :root)
compiles and renders correctly — only the nine that reference a name identical to their own,
with no independent declaration anywhere else in the file, are affected.
Environment
|
|
| Defect type |
Static (code) — confirmed by compiling the file with Tailwind; also observed as a live symptom |
| Commit |
975c7a02a963d80bf140bb269d773b4912b8e7b6 (upstream/main) |
| How measured (static) |
git fetch upstream && git rev-parse upstream/main, then reading packages/ui/src/styles/default.css at that commit and compiling it with Tailwind CSS 4.3.2 (postcss + @tailwindcss/postcss) |
| How measured (live symptom) |
A self-hosted deployment of the ghcr.io/chatbotxio/*:latest images, browser DevTools, checked on an earlier date than the commit above (see "What we did not verify" for the digest caveat) |
How to see it
packages/ui/src/styles/default.css, lines 65-73 (quoted in full above, inside @theme inline).
- Compile the file with Tailwind CSS 4.3.2 against a template using
shadow-xs and
tracking-wide, and inspect the compiled @layer theme block and the generated utility
classes.
- In a running instance, inspect any element with a
shadow-* class in DevTools:
getComputedStyle(el).boxShadow reads "none"; any tracking-wide element reads
letter-spacing: normal.
Evidence
Diff isolating exactly the nine circular lines (nothing else touched):
$ grep -v -E -e '--(tracking-normal|shadow(-[0-9a-z]+)?): var\(--(tracking-normal|shadow(-[0-9a-z]+)?)\);' default.css > a6-sombras.css
$ diff default.css a6-sombras.css | grep -c '^<'
9
Compiling default.css as-is with Tailwind 4.3.2 (entry: @import "tailwindcss"; @import "./default.css"; @source "./c.html";, c.html containing <div class="shadow-xs tracking-wide">) reproduces the circularity literally in the compiled @layer theme output —
Tailwind emits the reference verbatim rather than resolving it, because there is nothing else to
resolve it to:
--shadow-xs:var(--shadow-xs);
--tracking-normal:var(--tracking-normal);
And the generated utility for .shadow-xs still names the (unresolvable) variable:
.shadow-xs {
--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
The utility class itself is fine — the box-shadow color it depends on ultimately traces back to
Tailwind's own fallback default via --tw-shadow-color, but the theme-level --shadow-xs
that other parts of the design system (levels of elevation, default.css's own intent) would
reference to customize the value never resolves; in the browser, the net visible effect on the
compiled stylesheet actually served by a running deployment is box-shadow: none on
shadow-xs/shadow-md/shadow-lg/shadow, confirmed by inspecting a self-hosted deployment's
compiled CSS chunk directly, where the circular reference appears as a literal string
(--shadow-xs:var(--shadow-xs)) rather than a resolved value — meaning the deployed, minified
CSS carries the same defect, this isn't an artifact of our local recompilation.
Control (fixing it and reading the result back):
$ node compilar.mjs entrada.css salida.css # entrada.css imports tailwindcss + a6-sombras.css
$ grep -n -- '--shadow-xs:' salida.css
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
$ grep -n -- '--tracking-normal:' salida.css
--tracking-normal: 0em;
$ grep -c 'var(--shadow-xs)' salida.css
0
With the nine circular lines removed, Tailwind's own built-in defaults for these variables (from
tailwindcss/theme.css) take over, --shadow-xs resolves to a real shadow value, and no
var(--shadow-xs) circular reference remains anywhere in the compiled output. The .shadow-xs
utility now paints a visible shadow and .tracking-wide now computes letter-spacing: 0.35px
rather than normal.
Where it comes from
packages/ui/src/styles/default.css, lines 65-73, inside @theme inline (commit
975c7a02a963d80bf140bb269d773b4912b8e7b6). Confirmed absent from apps/builder/src/app/themes.css
(the file's 19 theme variants; rg -c for --shadow-/--tracking-normal there is 0), so this
is specific to the base theme file, not something every theme variant repeats or depends on.
Why we think this is a defect rather than the intended design
packages/ui/src/components/ui/button.tsx itself uses shadow-xs on every button variant
("bg-primary text-primary-foreground shadow-xs hover:bg-primary/90") — the component library
ships code that assumes shadows render. Nobody would deliberately ship a design system where the
button component's own shadow utility is invisible by construction; declaring a CSS custom
property as a reference to itself is a well-known way to accidentally end up with "no value" in
CSS (it usually happens when a value is meant to be "the same name, defined via Tailwind's own
--shadow-* theme defaults" and the local re-declaration inadvertently shadows rather than
extends those defaults). We found no comment, test, or PR describing "no shadows, ever" as an
intended visual choice.
Suggested fix
Remove the nine self-referential lines from @theme inline (the diff shown in Evidence). With
them gone, Tailwind CSS 4.3.2's own built-in defaults (node_modules/tailwindcss/theme.css:386
for --tracking-normal: 0em, :406-412 for --shadow-2xs through --shadow-2xl) apply
automatically — there is nothing else to add; the fix is a pure deletion. If upstream instead
wants custom (non-default) shadow/tracking values, those nine lines should assign literal values
rather than self-references, e.g. --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05); — but simply
deleting them (letting Tailwind's own defaults through) is already sufficient and was the
version we built and measured.
What we did not verify
- Whether upstream intends different (non-default) shadow depth/blur values than Tailwind's
stock ones — we verified that removing the circular lines restores Tailwind's defaults, not
that those defaults are the values upstream would pick if writing this from scratch.
- The exact deployed image digest for the live-deployment observation of the compiled CSS chunk
— it predates the commit cited in Environment and its sha256: was not recorded at the time;
the static (repository + local Tailwind compile) measurement above is commit-pinned and is
what this issue's static claim relies on.
- Any place that relies on the current zero-shadow, zero-extra-tracking appearance as a visual
baseline for a screenshot test or similar — we did not search test snapshots for shadow-shaped
assertions.
Related issues — none of these describes it
Searched gh search issues --repo ChatbotXIO/ChatbotX "<term>" for shadow none, shadow-xs,
and tracking circular (no --state filter); all three returned zero results. Witness that the
mechanism works: the same command with "bug" returns real issues (e.g. #1226, #1228).
Summary
packages/ui/src/styles/default.css's@theme inlineblock (lines 65-73) declares:Each of these nine declarations defines a custom property as a reference to itself. Per the CSS
custom-properties spec, a property that (directly or indirectly) references itself is invalid at
computed-value time, and the browser falls back to the property's initial value — which for
box-shadowisnoneand forletter-spacingisnormal. Tailwind then compilesshadow-xs,shadow-sm,shadow,shadow-md,shadow-lg,shadow-xl,shadow-2xlandtracking-tighterthrough
tracking-widestall in terms of these same nine circular variables, so every one ofthose utility classes renders with no visible effect, including on
button.tsx's ownshadow-xs(used by every button variant).Control that isolates this to these nine variables specifically, not
@theme inlineor Tailwindcompilation in general: every other variable resolved the same way in the same file (e.g.
--color-primary: var(--primary), where--primaryis independently declared in:root)compiles and renders correctly — only the nine that reference a name identical to their own,
with no independent declaration anywhere else in the file, are affected.
Environment
975c7a02a963d80bf140bb269d773b4912b8e7b6(upstream/main)git fetch upstream && git rev-parse upstream/main, then readingpackages/ui/src/styles/default.cssat that commit and compiling it with Tailwind CSS 4.3.2 (postcss+@tailwindcss/postcss)ghcr.io/chatbotxio/*:latestimages, browser DevTools, checked on an earlier date than the commit above (see "What we did not verify" for the digest caveat)How to see it
packages/ui/src/styles/default.css, lines 65-73 (quoted in full above, inside@theme inline).shadow-xsandtracking-wide, and inspect the compiled@layer themeblock and the generated utilityclasses.
shadow-*class in DevTools:getComputedStyle(el).boxShadowreads"none"; anytracking-wideelement readsletter-spacing: normal.Evidence
Diff isolating exactly the nine circular lines (nothing else touched):
Compiling
default.cssas-is with Tailwind 4.3.2 (entry:@import "tailwindcss"; @import "./default.css"; @source "./c.html";,c.htmlcontaining<div class="shadow-xs tracking-wide">) reproduces the circularity literally in the compiled@layer themeoutput —Tailwind emits the reference verbatim rather than resolving it, because there is nothing else to
resolve it to:
And the generated utility for
.shadow-xsstill names the (unresolvable) variable:The utility class itself is fine — the box-shadow color it depends on ultimately traces back to
Tailwind's own fallback default via
--tw-shadow-color, but the theme-level--shadow-xsthat other parts of the design system (levels of elevation,
default.css's own intent) wouldreference to customize the value never resolves; in the browser, the net visible effect on the
compiled stylesheet actually served by a running deployment is
box-shadow: noneonshadow-xs/shadow-md/shadow-lg/shadow, confirmed by inspecting a self-hosted deployment'scompiled CSS chunk directly, where the circular reference appears as a literal string
(
--shadow-xs:var(--shadow-xs)) rather than a resolved value — meaning the deployed, minifiedCSS carries the same defect, this isn't an artifact of our local recompilation.
Control (fixing it and reading the result back):
With the nine circular lines removed, Tailwind's own built-in defaults for these variables (from
tailwindcss/theme.css) take over,--shadow-xsresolves to a real shadow value, and novar(--shadow-xs)circular reference remains anywhere in the compiled output. The.shadow-xsutility now paints a visible shadow and
.tracking-widenow computesletter-spacing: 0.35pxrather than
normal.Where it comes from
packages/ui/src/styles/default.css, lines 65-73, inside@theme inline(commit975c7a02a963d80bf140bb269d773b4912b8e7b6). Confirmed absent fromapps/builder/src/app/themes.css(the file's 19 theme variants;
rg -cfor--shadow-/--tracking-normalthere is 0), so thisis specific to the base theme file, not something every theme variant repeats or depends on.
Why we think this is a defect rather than the intended design
packages/ui/src/components/ui/button.tsxitself usesshadow-xson every button variant(
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90") — the component libraryships code that assumes shadows render. Nobody would deliberately ship a design system where the
button component's own shadow utility is invisible by construction; declaring a CSS custom
property as a reference to itself is a well-known way to accidentally end up with "no value" in
CSS (it usually happens when a value is meant to be "the same name, defined via Tailwind's own
--shadow-*theme defaults" and the local re-declaration inadvertently shadows rather thanextends those defaults). We found no comment, test, or PR describing "no shadows, ever" as an
intended visual choice.
Suggested fix
Remove the nine self-referential lines from
@theme inline(the diff shown in Evidence). Withthem gone, Tailwind CSS 4.3.2's own built-in defaults (
node_modules/tailwindcss/theme.css:386for
--tracking-normal: 0em,:406-412for--shadow-2xsthrough--shadow-2xl) applyautomatically — there is nothing else to add; the fix is a pure deletion. If upstream instead
wants custom (non-default) shadow/tracking values, those nine lines should assign literal values
rather than self-references, e.g.
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);— but simplydeleting them (letting Tailwind's own defaults through) is already sufficient and was the
version we built and measured.
What we did not verify
stock ones — we verified that removing the circular lines restores Tailwind's defaults, not
that those defaults are the values upstream would pick if writing this from scratch.
— it predates the commit cited in Environment and its
sha256:was not recorded at the time;the static (repository + local Tailwind compile) measurement above is commit-pinned and is
what this issue's static claim relies on.
baseline for a screenshot test or similar — we did not search test snapshots for shadow-shaped
assertions.
Related issues — none of these describes it
Searched
gh search issues --repo ChatbotXIO/ChatbotX "<term>"forshadow none,shadow-xs,and
tracking circular(no--statefilter); all three returned zero results. Witness that themechanism works: the same command with
"bug"returns real issues (e.g. #1226, #1228).