Skip to content

@theme inline declares --shadow-*/--tracking-normal as var() references to themselves, so every shadow-* utility computes to no shadow and every tracking-* utility computes to normal letter-spacing #1241

Description

@rawdaymx

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

  1. packages/ui/src/styles/default.css, lines 65-73 (quoted in full above, inside @theme inline).
  2. 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.
  3. 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions