Reference

Diagnostic codes

Every error and warning the compiler emits carries a stable TLS#### identifier. They show up at the start of every diagnostic line:

✗ [TLS0404 · no-tailwind] app/Card.tsx:3:14
    Tailwind classes detected — use tl.create() instead.
    docs: https://traceless-style.dev/diagnostics#tls0404

The codes never change once published. Use them in CI greps, in issue titles, and in suppression directives. If a code's behavior changes in a way that would break a downstream grep, we add a new code instead of mutating the old one.

Quick lookup

CodeSeverityTitle
TLS0001errorVariable in style object
TLS0002errorUnexpected token in tl.create body
TLS0003errorInvalid key in style object
TLS0101errorUnknown CSS property
TLS0102errorSuspicious value (CSS injection guard)
TLS0103errorbackground-clip:text + background: shorthand conflict
TLS0201errorUnknown variant key
TLS0202errorVariant value must be an object
TLS0301warningToken redeclared
TLS0302errorInvalid keyframe step name
TLS0401errorInline style attribute
TLS0402errorString className
TLS0403errorCSS module import
TLS0404errorTailwind utility class
TLS0501errorText contrast below WCAG 2.1 AA
TLS0502warningText contrast below WCAG 2.1 AAA
TLS0503errorUI component contrast below 1.4.11
TLS0504errorFocus indicator below 2.4.13
TLS0505errorGradient-text stop fails contrast
TLS0506warningImage background — runtime audit advised
TLS0601errorInvalid traceless-style.config.js

The numeric ranges are:

RangeDomain
TLS0001TLS0099Parser / AST
TLS0100TLS0199Property allowlist + value validation
TLS0200TLS0299Variants + tl.extend
TLS0300TLS0399Tokens, themes, keyframes
TLS0400TLS0499Lint rules
TLS0500TLS0599Contrast / accessibility (WCAG, APCA)
TLS0600TLS0699Build wiring (config, framework integration)

Parser / AST (TLS00xx)

TLS0001

Variable in style object. The compiler's strict literal-only parser rejected an identifier or expression where it expected a literal string, number, or nested object.

const padding = "1rem";

// ✗ TLS0001 — `padding` is an identifier.
tl.create({ btn: { padding } });

// ✓ Use a literal:
tl.create({ btn: { padding: "1rem" } });

// ✓ Or wrap the runtime value in a token:
const tokens = tl.defineTokens({ space: { md: "1rem" } });
tl.create({ btn: { padding: tl.cssVar("space-md") } });

The parser is intentionally strict — see Defense-in-depth value validation. Allowing variables would force the compiler to evaluate arbitrary JavaScript at build time, which is a security and maintainability hazard. Tokens give you the "runtime configurability" you'd want from a variable.

TLS0002

Unexpected token in tl.create body. The argument to a tl.<method>(...) call must be a single object literal. The parser hit something it can't interpret — usually a spread (...obj), a function call, or a stray comma.

const base = { padding: "1rem" };

// ✗ TLS0002 — spread isn't supported in tl.create arguments.
tl.create({ btn: { ...base, color: "red" } });

// ✓ Inline the keys, or compose at the className site:
const $ = tl.create({
  base: { padding: "1rem" },
  btn:  { padding: "1rem", color: "red" },
});

If you genuinely want to share a sub-object, declare two groups and combine their classes via tl.merge() at the use site.

TLS0003

Invalid key in style object. Keys must be identifiers, string literals, or numbers. Computed keys ([someExpr]:) and getter/ setter shorthand are not supported.


Property / value (TLS01xx)

TLS0101

Unknown CSS property. The property name isn't in the curated allowlist (~250 CSS Color 4 / Layout / Typography properties plus vendor prefixes and CSS variables). Common cause: a typo.

tl.create({ btn: {
  colour: "red",        // ✗ TLS0101 — did you mean 'color'?
  fontSizz: "16px",     // ✗ TLS0101 — did you mean 'fontSize'?
} });

The diagnostic includes a Levenshtein-suggested replacement when one exists. Custom property names (--brand-primary) and vendor prefixes (webkitTransform, -moz-appearance) are accepted without warning.

TLS0102

Suspicious value (CSS injection guard). A value contains characters the CSS-rule emitter blocks at the value boundary — ;, }, </, */, \\, ASCII control characters (0x00–0x1F except TAB/LF/CR), or invisible Unicode (zero-width / bidi-control).

This is defense-in-depth. The current AST parser already rejects non-literal values, so an attacker would need a parser bug to inject hostile values. The injection guard exists so that a future parser change can never accidentally widen the attack surface.

TLS0103

background-clip:text + background: shorthand conflict. The CSS background: shorthand resets background-clip to its initial value (border-box). When the same group sets background-clip: text, atomic-CSS cascade order can silently undo the clip and produce invisible gradient text.

// ✗ TLS0103
tl.create({ heroTitle: {
  background:     "linear-gradient(120deg, #6366f1, #ec4899)",
  backgroundClip: "text",
  color:          "transparent",
} });

// ✓ Use the longhand — backgroundImage doesn't reset clip.
tl.create({ heroTitle: {
  backgroundImage: "linear-gradient(120deg, #6366f1, #ec4899)",
  backgroundClip:  "text",
  color:           "transparent",
} });

We added this guard after a real production bug — gradient hero text appeared correctly in development and broke when an unrelated component emitted its own background: rule later in the cascade.


Variants (TLS02xx)

TLS0201

Unknown variant key. The key starts with _ but isn't a registered variant. Built-in variants are listed in the variants table. Project-specific variants must be registered via tl.extend({ variants: { ... } }).

tl.create({
  btn: {
    color: "blue",
    _hovered: { color: "red" },   // ✗ TLS0201 — did you mean '_hover'?
  },
});

TLS0202

Variant value must be an object. A variant key (_dark, _hover, …) must map to a nested style-object containing the rules to apply when the variant matches.

// ✗ TLS0202
tl.create({ btn: { _hover: "red" } });

// ✓
tl.create({ btn: { _hover: { color: "red" } } });

Tokens / themes / keyframes (TLS03xx)

TLS0301

Token redeclared. tl.defineTokens was called more than once with the same export key. The second declaration's light value is ignored (first-write-wins); only darkValue is updated when missing.

If two files genuinely declare the same token, this is a sign your project should consolidate them. A single tokens.ts file shared across the project is the recommended pattern.

TLS0302

Invalid keyframe step name. Keyframe step names must be from, to, or a <n>% value.

// ✗ TLS0302
tl.keyframes("slide", {
  start: { x: 0 },        // not a percentage / from / to
  end:   { x: 100 },
});

// ✓
tl.keyframes("slide", {
  from: { transform: "translateX(0)" },
  to:   { transform: "translateX(100px)" },
});

Lint (TLS04xx)

TLS0401

Inline style attribute. style={{...}} and style="..." bypass the compiler entirely. Inline styles can't be deduplicated, can't be audited for accessibility, and can't be type-checked against the property allowlist.

// ✗ TLS0401
<div style={{ padding: 16, color: "red" }}>...</div>

// ✓
const $ = tl.create({ box: { padding: "1rem", color: "red" } });
<div className={$.box}>...</div>

This rule cannot be disabled. lint: false in the config keeps noInlineStyles on — there is no legitimate use of style= in a traceless-style project.

TLS0402

String className. A className="some-class" literal opts out of every guard the compiler provides — type safety, atomic dedup, the property allowlist, contrast validation.

// ✗ TLS0402
<button className="primary-button">Save</button>

// ✓
const $ = tl.create({ primary: { background: "#3b82f6", color: "#fff" } });
<button className={$.primary}>Save</button>

Strings produced by tl.create(), tl.merge(), tl.cx(), or library class accessors ($.btn) are recognized — only literal string expressions are flagged.

TLS0403

CSS module import. Importing *.module.css mixes two stylesheet systems and breaks atomic-CSS dedup. Module-scoped class names are opaque to traceless-style — it can't audit them for contrast or property correctness, and it can't deduplicate them with its own atoms.

TLS0404

Tailwind utility class. Tailwind class strings detected in a className. Tailwind's atomic CSS duplicates traceless-style's purpose; mixing the two doubles your bundle size.

If you're migrating from Tailwind incrementally, see Migrating from Tailwind. Until the migration is complete, set lint.noTailwind: false in the config to silence the rule per-file rather than disabling globally.


Contrast / accessibility (TLS05xx)

These come from the WCAG contrast validator. Each carries a citation to the specific WCAG criterion that defines the threshold.

TLS0501

Text contrast below WCAG 2.1 §1.4.3 (AA). A foreground/background pair measures below 4.5:1 (or 3:1 for large text — ≥18pt regular or ≥14pt bold). This is the legal floor for Section 508 (US) and EN 301 549 (EU). Build fails by default; contrast.strict: false demotes to warning.

The diagnostic includes:

  • Measured ratio (e.g. 2.46:1).
  • APCA Lc score (advisory, WCAG 3 working draft).
  • A suggested replacement color computed via OKLCH-space search (preserves the designer's hue and chroma intent).

TLS0502

Text contrast below WCAG 2.1 §1.4.6 (AAA). Below the enhanced contrast tier (7:1 / 4.5:1). AAA is best-effort — not legally required — so this is a warning unless contrast.strictAAA: true.

TLS0503

UI component contrast below WCAG 2.1 §1.4.11. A borderColor, caretColor, accentColor, textDecorationColor, or extracted boxShadow color (when the shadow is "informational" — has spread or is inset) measures below 3:1 against its background. UI affordances must remain identifiable to low-vision users.

TLS0504

Focus indicator below WCAG 2.2 §2.4.13. An outlineColor measures below 3:1 against the surface. Focus rings need to remain visible for keyboard users.

TLS0505

Gradient-text stop fails contrast. When color: transparent + background-clip: text is in use, every gradient stop (and sampled midpoint, when gradientSampleCount > 0) fills part of the glyph. A single low-contrast stop renders that slice of text unreadable.

TLS0506

Image background — runtime audit advised. Text sits on a url(...)-backed background. Pixel-level contrast can't be verified at build time. Add a solid layer behind the text (opaque overlay, linear-gradient with high-contrast endpoints) or audit at runtime with axe-core / Pa11y.


Build / config (TLS06xx)

TLS0601

Invalid traceless-style.config.js. The config file failed to load (syntax error, missing module, unsupported export shape) or contains an unknown top-level key.

The full schema is in Configuration. Unknown keys trigger this code with a "did you mean" suggestion.


Suppressing diagnostics

There's no // traceless-style-disable-line comment yet — instead, diagnostics are suppressed at the source where they're meaningful:

DiagnosticSuppression
TLS0501TLS0506 (contrast)_skipContrast: true (all categories) or _skipContrast: "ui" / "dark" / etc. per-category at the group level.
TLS0401TLS0404 (lint)Disable the rule globally in traceless-style.config.js (e.g. lint.noTailwind: false).
TLS0101 / TLS0102 (property)Use a CSS variable (--brand) or vendor prefix instead. There is no opt-out — these guards keep the bundle safe.

When in doubt, fix the diagnostic rather than suppress it. The suggestions in each error are computed to preserve design intent — they're rarely a bigger change than disabling the rule.

See also