Skip to content

Tailwind v4 css-tailwind export rejects digit-prefix token names that Tailwind v4 itself uses #71

@szainmehdi

Description

@szainmehdi

Summary

The css-tailwind exporter (Tailwind v4 @theme {}) validates token names against ^[a-zA-Z][a-zA-Z0-9-]*$, which rejects names starting with a digit. This is more restrictive than CSS spec actually requires for custom properties, AND it rejects names that Tailwind v4 itself uses by default (--radius-2xl, --radius-3xl, --spacing-2, etc.).

Reproducer

---
name: Repro
colors:
  primary: "#ED5A2F"
spacing:
  2xs: 2px
  xs: 4px
---

## Overview

Minimal repro.
$ npx --yes @google/design.md export --format css-tailwind DESIGN.md
{"error":"Token name \"2xs\" is not a valid CSS identifier for Tailwind v4 export (must match /^[a-zA-Z][a-zA-Z0-9-]*$/)."}

The same input lints clean and exports fine via --format tailwind (Tailwind v3 JSON) and --format dtcg.

Why this should work

  1. CSS spec allows it. Per css-variables-1 §2, custom property names are --<ident>. CSS identifiers (css-syntax-3 §4.1.1) allow digits anywhere except as the literal first character of an unprefixed ident — but the -- prefix means the post-prefix part is treated as <custom-property-name>, which permits digit-leading content.

  2. Tailwind v4 itself uses these. Tailwind v4's default theme defines --radius-2xl, --radius-3xl, --radius-4xl, --spacing-0, --spacing-0\.5, --spacing-2, etc. A tool that emits Tailwind v4 @theme {} shouldn't enforce a stricter naming rule than Tailwind v4's own conventions.

  3. The other emitters (tailwind, dtcg) accept the same names. It's only the v4 path that blocks.

Source

packages/cli/src/linter/tailwind/v4/handler.ts:42:

const VALID_TOKEN_NAME = /^[a-zA-Z][a-zA-Z0-9-]*$/;

Proposed fix

-const VALID_TOKEN_NAME = /^[a-zA-Z][a-zA-Z0-9-]*$/;
+const VALID_TOKEN_NAME = /^[a-zA-Z0-9][a-zA-Z0-9-]*$/;

(Or relax further — allow _ and dot-escape — if you want full Tailwind v4 parity for fractional spacing.)

Happy to send a PR if the proposed fix is the direction you want.

Context

Discovered while wiring @google/design.md into a multi-tier scale (2xs/xs/sm/md/lg/xl/2xl/3xl/4xl/5xl/6xl) that mirrors Tailwind v4's defaults. The lint passes and tailwind/dtcg exports both work; only css-tailwind blocks, which is the one we actually need for a Tailwind v4 build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions