Skip to content

feat: upgrade to Tasty v3 and ESLint plugin v1 - #1275

Merged
tenphi merged 14 commits into
mainfrom
feat/tasty-v3-canary
Aug 6, 2026
Merged

feat: upgrade to Tasty v3 and ESLint plugin v1#1275
tenphi merged 14 commits into
mainfrom
feat/tasty-v3-canary

Conversation

@tenphi

@tenphi tenphi commented Aug 3, 2026

Copy link
Copy Markdown
Member

Do not merge as-is@tenphi/tasty points at a snapshot build. This exists to exercise the Tasty v3 release against a real design system before it ships, and to show what the migration actually costs.

Upstream: tenphi/tasty#227 · tenphi/eslint-plugin-tasty#38

Result

main this branch
Tests 1139 pass 1139 pass
Type errors 13 13 (byte-identical, all pre-existing and unrelated to Tasty)
oxlint clean clean

No style values needed changing. v3's stricter directional syntax — a group naming directions takes one value, so padding: '2x 4x top right' becomes padding: '2x top, 4x right'is not violated anywhere in this codebase. I verified that by running the v3 ESLint plugin over src/, not by grepping.

Migration

getCssTextForNodegetCSSTextForNode — test helpers and the ESLint-plugin probe. Mechanical.

Props is no longer exported by Tasty. It was never a Tasty concept, just Record<string, any>. Worth flagging: the UI Kit re-exported it publicly, so this would have been a breaking change for our consumers. It is now declared in src/props.ts and still exported from the package root, leaving our own API unchanged. 16 files repointed.

Text declares its own block prop. It was inherited from Tasty's BaseProps, which v3 dropped as an unconsumed prop — but the UI Kit does consume it, as a mod and in the 'ellipsis | block' style branch. Declaring it locally preserves the behavior.

Title declares its own inline prop, deprecated. Same origin, but here the prop was genuinely dead: TitleElement has no inline mod and hardcodes display: 'block', so forwarding it did nothing except risk an invalid DOM attribute. Kept on the type so the public prop surface is unchanged; no longer forwarded.

Two things worth knowing

The ESLint plugin stays on ^0.11.2. Its v3 canary cannot be published: npm trusted publishing for @tenphi/eslint-plugin-tasty rejects the snapshot workflow's OIDC token with a 404 on the final PUT, after the build and provenance signing succeed. Adding environment: release to match release.yml did not fix it, so the allowed-workflow list on the npm package needs updating. Once that lands, this PR needs one line changed.

The old plugin does not flag v3 code (it validates the v2 at-rule spellings, and v3's kebab-case keys fall through an unconditional accept), so lint is clean either way — it just isn't validating anything v3-specific yet.

This PR found a regression in the v3 ESLint plugin. An earlier version of valid-directional-modifier reported 20 errors here, every one a false positive: verticalAlign: 'bottom', textAlign: 'left', transformOrigin: 'top center', transition: 'left 0.2s, top 0.2s'. Direction words are ordinary CSS values for a long tail of properties, so the rule's property gate turned out to be load-bearing. Fixed upstream, with all 20 cases added as regression tests. That is the payoff for testing on a real project rather than a fixture.

🤖 Generated with Claude Code


Note

Medium Risk
Minor semver API change for Text/Title props plus a major styling dependency bump and intentional bundle size increase; behavior is covered by new display tests but consumers must update usages.

Overview
Upgrades @tenphi/tasty to v3 and @tenphi/eslint-plugin-tasty to v1, with the mechanical renames (getCSSTextForNode) and a local Props type in src/props.ts so the package still re-exports it after Tasty dropped it.

Breaking API cleanup: removes block on Text and inline on Title in favor of display; Text ellipsis still forces block display. Docs and display-prop.test.tsx cover the migration.

Registers 12 color tokens in tasty.config.ts (were only in augment types), fixes Storybook inset to v3 comma syntax ('2x bottom, 4x left'), and raises size-limit budgets (~462 kB full / ~123 kB tree-shake) for Tasty v3’s larger bundle.

Reviewed by Cursor Bugbot for commit 2e4ca4e. Bugbot is set up for automated code reviews on this repo. Configure here.

Points `@tenphi/tasty` at the v3 snapshot build and applies the migration, so the
v3 release gets exercised against a real design system before it ships. Not for
release — the dependency is a snapshot.

Result: the full test suite passes (1139 tests) and the type-error count is
unchanged from main (13, all pre-existing and unrelated to Tasty).

Migration:

- `getCssTextForNode` -> `getCSSTextForNode`.
- `Props` is no longer exported by Tasty. It was never a Tasty concept, just
  `Record<string, any>`, but the UI Kit re-exported it publicly — so it is now
  declared in `src/props.ts` and still exported from the package root, leaving
  the UI Kit's own API unchanged.
- `Text` declares its own `block` prop. It came from Tasty's `BaseProps`, which
  v3 dropped as unconsumed; the UI Kit does consume it, as a mod and in the
  `'ellipsis | block'` style branch.
- `Title` declares its own `inline` prop, deprecated. Same origin, but genuinely
  dead: `TitleElement` has no `inline` mod and hardcodes `display: 'block'`, so
  forwarding it only risked an invalid DOM attribute. Kept on the type, no
  longer forwarded.

No style values needed changing — v3's one-value-per-directional-group rule is
not violated anywhere in the codebase.

The ESLint plugin stays on ^0.11.2: its v3 canary cannot be published yet, since
npm trusted publishing for that package rejects the snapshot workflow's token.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cube-ui-kit Ready Ready Preview Aug 6, 2026 9:11am

Request Review

@changeset-bot

changeset-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2e4ca4e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cube-dev/ui-kit Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📦 NPM canary release

Deployed canary version 0.0.0-canary-b7acb99.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🧪 Storybook is successfully deployed!

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🏋️ Size limit report

Name Size Passed?
All 449.36 KB (+0.77% 🔺) Yes 🎉
Tree shaking (just a Button) 118.94 KB (+3.2% 🔺) Yes 🎉

Click here if you want to find out what is changed in this build

@tenphi

tenphi commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Size check fails — and it is a real finding, not noise

I measured both sides locally rather than bumping the limit, because this is the number the release should be judged on.

Entry main (tasty 2.11.0) v3 canary Δ limit
All 446.93 kB 450.33 kB +3.40 kB 460 — passes both
Tree shaking (just a Button) 117.61 kB 121.38 kB +3.77 kB 118 — fails by 3.38 kB

So Tasty v3 costs a consumer roughly 3.4–3.8 kB gzipped, and it lands on an entry that had only 0.39 kB of headroom.

The dominant contributor is dev-warning strings. v3 adds a fair amount of diagnostic prose — the directional-syntax warning, the handler-displacement warning, the chunk-conflict warning — and those live in modules that ship in every bundle. They cannot be stripped, because isDevEnv() is deliberately evaluated at runtime with bracket notation so one build works in both dev and production. Tasty's own pnpm size needed its limits raised for the same reason (+1.7 to +2.1 kB across its bundles).

I have deliberately not raised .size-limit.cjs here. Three options, in the order I would consider them:

  1. Trim the warning prose upstream. The messages are long by design — they explain why, not just what. Shortening the three longest would likely recover most of the 3.8 kB.
  2. Gate the strings behind a build-time flag so bundlers can drop them, accepting that Tasty then needs separate dev/prod builds — a real architectural change, since the current single-build design is intentional.
  3. Raise the limit and accept the cost.

That is a call for the Tasty release, not for this PR, which is why I left the check red rather than papering over it.


Everything else is green: Tests & lint pass, Build & canary release passes, CodeQL and Snyk pass, Storybook publishes. Type-error count is byte-identical to main (13, all pre-existing).

tenphi and others added 2 commits August 3, 2026 17:34
Tasty v3 costs +3.77 kB on the Button entry (117.61 -> 121.38 kB gzipped). Its
new dev diagnostics — directional syntax, handler displacement, chunk conflicts —
ship in every bundle, because isDevEnv() is evaluated at runtime so a single
build serves dev and production. That entry only had ~370 B of headroom, which
the file's own comment had already flagged as likely to trip next.

Raised to 123 kB rather than higher, keeping headroom small so real bloat still
trips the budget. The All entry is unaffected (450.33 kB against 460 kB).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tenphi

tenphi commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Limit bumped in 83a3ec7Tree shaking (just a Button) goes from 118 kB to 123 kB, so both size entries pass:

Entry main this branch limit
All 446.93 kB 450.33 kB 460 kB
Tree shaking (just a Button) 117.61 kB 121.38 kB 123 kB

Headroom stays deliberately small (~1.6 kB) so real bloat still trips the budget, matching the file's existing philosophy. The old comment on that entry had already called it — "only ~370 B of headroom, so this one is likely to trip next even though it passes today."

Also merged main in, so this is up to date with #1276.

The +3.77 kB is Tasty v3's new dev diagnostics, which ship in every bundle because isDevEnv() is evaluated at runtime so a single build serves dev and production. Recording it here rather than treating it as noise: if that cost is ever worth clawing back, shortening the three longest messages upstream is the cheapest lever, and moving to a build-time flag is the thorough one.

Remaining checks are the two Chromatic gates, which need a human to accept or reject the visual diffs — build #3645, 916 stories. That is the last unverified thing about this migration: unit tests and typecheck both came back identical to main, so I would expect zero visual changes, but only Chromatic can confirm it.

Styles.stories.tsx had `inset: '2x bottom 4x left'` — the positional form v3
removes. Now `'2x bottom, 4x left'`. Verified against the v3 runtime that the
comma form reproduces what v2 rendered (`auto auto 16px 32px`); the old form now
drops the `4x` and renders `auto auto 16px 16px`, so this was a real rendering
change, not just a lint warning.

The linter had missed it. Story files were effectively unchecked, because a
Storybook story's `args.styles` matched none of the plugin's detection
heuristics — the enclosing variable is named after the story and there is no
Tasty call. Fixed in tenphi/eslint-plugin-tasty#38, which is what found this.

Enabling story linting also surfaced 12 color tokens declared in
`src/tasty-augment.d.ts` but absent from `tasty.config.ts`, which the plugin
reads — so they were reported as unknown. Added, with a note to keep the two
lists in sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0.0.0-snapshot.33e181d -> 0.0.0-snapshot.cdc961c. Picks up two things the older
snapshot predates: the `fade: true` typing fix, and main's #251 injector change
(remove injected styles in text-injection mode), which is a runtime change this
branch had not yet exercised.

Unchanged against the older canary: 1139 tests pass, 13 type errors (all
pre-existing), zero tasty lint errors. Sizes move by under 0.5 kB
(450.33 -> 450.81 kB and 121.38 -> 121.79 kB), both inside their limits.
Both packages are released, so this moves off the canary snapshots onto real
versions: @tenphi/tasty ^3.0.0 and @tenphi/eslint-plugin-tasty ^1.0.0.

The changeset becomes a `minor` and drops the "not for release" caveat — the
branch was previously pinned to 0.0.0-snapshot builds purely to validate the
migration ahead of the release.

Verified against the released versions: 1139 tests pass, 13 type errors (all
pre-existing), zero tasty lint errors, and both size entries inside their limits
(450.81/460 kB and 121.79/123 kB).

Plugin v1 lints Storybook `args.styles` and `styles={{…}}` JSX props for the
first time, so story files are now covered — which is how the `inset` violation
this branch fixes was found in the first place.
@tenphi tenphi changed the title test: validate against the Tasty v3 canary feat: upgrade to Tasty v3 and ESLint plugin v1 Aug 4, 2026
@tenphi

tenphi commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Both packages are released, so this is off the canaries and onto real versions:

was now
@tenphi/tasty 0.0.0-snapshot.cdc961c ^3.0.0
@tenphi/eslint-plugin-tasty ^0.11.2 ^1.0.0

The changeset is now a minor and no longer says "not for release" — this is mergeable as a real upgrade.

Verified against the released versions:

  • 1139 tests pass, 1 skipped
  • 13 type errors, all pre-existing on main
  • 0 tasty lint errors; the 8 remaining warnings (consistent-token-usage ×7, prefer-custom-property-syntax ×1) all pre-date this branch
  • All 450.81 kB / 460 kB · Tree shaking (just a Button) 121.79 kB / 123 kB

One thing worth noting: plugin v1 lints Storybook args.styles and styles={{…}} JSX props for the first time. Story files had been silently unchecked — no detection heuristic reached them — which is exactly how the inset: '2x bottom 4x left' violation this PR fixes survived. Stories are now covered, and they came back clean apart from the pre-existing warnings above.

It was committed into this PR by accident — swept in by a `git add -A` in the
size-limit commit, which is unrelated to it — and the design it describes is no
longer wanted.

Removed entirely rather than untracked: nothing references it, and it was never
meant to be part of the v3 upgrade.
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

Brings in the palette rework (#1277) and the DatePicker period pickers (#1279).
Three conflicts, all in build config:

- package.json: keeps `@tenphi/tasty` at `^3.0.0` (the point of this branch, now
  resolving to 3.0.1) and takes main's newer `@tenphi/glaze` 1.3.0.
- .size-limit.cjs: the two increases stack. main had already gone 118 -> 119 kB
  for the ~400 B that tasty 2.11.0 -> 2.11.2 added; v3 costs a further ~3.8 kB.
  Re-measured on the merged tree at 121.79 kB and set to 123 kB rather than
  carrying either side's number forward.
- pnpm-lock.yaml: regenerated.

Verified against main's own baseline: the 13 type errors are byte-identical to
`origin/main`'s, so the merge introduces no regressions. 1200 tests pass (main
added 61), zero tasty lint errors, both size entries inside their limits
(451.54/460 kB and 121.79/123 kB).

Notably main's new palette and DatePicker code had never been built against v3,
and needed no changes.
CI caught what my local check missed: the `All` entry is 460,161 B against a
460,000 B limit — over by 161 B. Raised to 462 kB, keeping headroom small.

My earlier local measurement was wrong. `size-limit` bundles the built
`./dist/index.js`; it does not build. I ran it after merging main without
rebuilding, so I measured a pre-merge bundle and read 451.54 kB instead of the
real 460.16 kB. Added a note in the config so the next person does not repeat it.
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

tenphi added 2 commits August 6, 2026 10:42
Use `display` instead: `<Text display="block">`, `<Title display="inline">`.

Both were inherited from Tasty's `BaseProps` until v3 dropped them, at which
point this branch re-declared them locally. `display` already covers the case on
every Tasty component, so a bespoke boolean per component is redundant.

`inline` on Title was already inert — destructured and discarded, with
TitleElement hardcoding `display: 'block'`. Removing it changes nothing at
runtime.

`block` on Text was real: it drove a mod feeding the `'ellipsis | block'` display
branch. That branch is now `ellipsis` alone, so `<Text ellipsis>` still renders
as a block — pinned by a new test, since folding that branch is the part most
likely to regress.

Verified before removing that `display` works on both components, and after that
the removed props are type errors while `display` is accepted. No internal usages
existed to migrate.
@cube-dev/ui-kit is pre-1.0 (0.154.1), so a major changeset would have jumped it
straight to 1.0.0. Breaking changes ride in minors until then.

`changeset status` now reports a single minor bump and nothing at major.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant