Repository navigation
Conversation
…ents Add prefer-text-components. It reports a Tailwind font size on a plain p, h1 to h6, or span element and names Text.<element> with its default role. For span it also names a role utility. It reads className strings, template literals, ternary branches and cn/clsx/cx/twMerge/twJoin arguments, has no autofix, and is not in recommended because Text.* needs AppShell 1.19.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nents Read the arguments of classNames(), and unwrap as, satisfies and non-null expressions. The span message now names a 14px role utility as well as a 12px one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code Metrics Report
Details | | main (f9acc22) | #667 (50e55a9) | +/- |
|---------------------|----------------|----------------|------|
| Coverage | 89.6% | 89.6% | 0.0% |
| Files | 269 | 269 | 0 |
| Lines | 7728 | 7728 | 0 |
| Covered | 6930 | 6930 | 0 |
+ | Test Execution Time | 3m46s | 3m23s | -23s |Reported by octocov |
Resolve docs-manifest.json by regenerating with pnpm docs:sync.
…nto feat/eslint-prefer-text-components # Conflicts: # docs-manifest.json
|
I think we can initially limit this rule to string literals written directly in This does not mean limiting detection to a single class name. All of the following should be checked by splitting the string on whitespace and detecting <p className="text-sm hoge" />
<p className={"text-sm hoge"} />
<p className={`text-sm hoge`} />We can document that |
…-text-components Check a string literal, a string literal in braces, or a template literal without interpolation. cn() and other class helpers, ternaries, variables and interpolated templates are out of scope for now. The erp-kit scaffold still gives 264 reports in 91 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks. I narrowed the rule as you suggested in 6f66ac3.
🤖 Addressed by Claude Code |
…nto feat/eslint-prefer-text-components # Conflicts: # docs-manifest.json
interacsean
left a comment
There was a problem hiding this comment.
Hey @wemra3 — I am wondering if this can be expanded a little bit further for other cases where consumers are using a utility class on an html element that could be replaced by a text component.
Claude suggested:
h1 to h6: report any weight, leading-* or tracking-* when the class string has no role utility. A heading has no inline-emphasis use, so false positives should be close to zero. This needs a second message, because there’s no size to quote in found.
span / p with tracking-* or leading-* (and no role utility): worth reporting too. In the scaffold, font-medium uppercase tracking-wide on a span in language-switcher.tsx is an eyebrow label, i.e. a label role built by hand.
span / p with only a weight: keep these allowed. inside a sentence is real emphasis (9 spans in the scaffold, mostly in revision-history drawers). The 7
elements look like label-md, but they’re ambiguous, so leave them out of v1.
Test: flip test line 29 to an invalid case.
…-text-components When the class string has no typography role, report any weight, leading-* or tracking-* on h1 to h6, and leading-*, tracking-* or a weight other than font-semibold on p and span. font-semibold alone stays allowed on p and span as emphasis inside a sentence. On the erp-kit scaffold this adds 12 headings and 16 p and span elements (292 in all). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ext-components Report font-[600] like a named weight, decide headings with /^h[1-6]$/, and word the p/span message so the font-semibold note fits every report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nto feat/eslint-prefer-text-components
|
Thanks for the proposal. I checked it on the latest erp-kit scaffold and added it in this PR, with one change for
The scaffold result is now 292 reports in 97 files (264 sizes, 12 headings, 16 🤖 Addressed by Claude Code |
Motivation
Part of tailor-inc/platform-planning#2214. In the 2026-10-26 grooming and sync, Seiya asked for lint rules that guide consumer code to the new styles, and Sean said lint could be high priority. #651 added the first rule (
no-deprecated-tokens). This PR adds the second one.AppShell 1.19.0 added element components such as
Text.pandText.h3. The styling guide says not to style a plainp,h1toh6, orspanwithtext-smortext-xs. Nothing checks this today.Summary
prefer-text-components. On a plainp,h1toh6, orspanelement, it reports a Tailwind font size, and text styled by hand without a typography role. It names the element component and its default role.<p className="text-sm">→ use<Text.p>(default rolebody-md)<h3 className="text-lg font-semibold">→ use<Text.h3>(default roleheading-sm), and set another role withvariantif needed<span className="text-xs">→ use<Text.span>(default rolelabel-md), or a role utility such astext-label-mdortext-body-smwhen the app styles the element itselftext-xstotext-9xl, arbitrary sizes inpx,remorem, andtext-[length:...].h1toh6with no role utility: any weight,leading-*ortracking-*. For example,<h3 className="font-semibold">→<Text.h3>. A heading has no use for inline emphasis.pandspanwith no role utility:leading-*,tracking-*, or a weight other thanfont-semibold.font-semiboldalone stays allowed, because the typography decision record allows it for emphasis inside a sentence and notfont-medium.className:"...",{"..."}, or a template literal without interpolation. It splits the string on whitespace, sotext-smis found among other classes.cn(...), ternaries, variables and constants, and template literals with interpolation are out of scope for now (changed after review).text-smcan bebody-md,label-mdorheading-sm.recommended. Apps enable it in their own config. The rule reports many places in existing apps (see below), andText.*exists only from AppShell 1.19.0. In an app on an earlier version, the rule would name components that do not exist yet. The README states this.splitVariantsmoves fromno-deprecated-tokensto a shared module. Its behavior does not change.decisions/typography-roles.md.Tried on the ERP-kit scaffold
I packed the plugin with
pnpm pack, installed the tarball in a new project with oxlint 1.89.0 (the version the ERP-kit frontend uses), and used the config from the README. Onpackages/erp-kit/templates/scaffoldin tailor-platform/erp-kit (origin/main,3bcf8782), the rule reports 292 warnings in 97 files, and the exit code is 0:p180,span37,h333,h27,h15,h42)<h3 className="font-semibold">in the create forms and<h3 className="font-medium">in the empty statepandspanstyled by hand: 16. 15 usefont-medium(card titles such as<p className="font-medium">, and author names in the revision history), and 1 isfont-medium uppercase tracking-wideinlanguage-switcher.tsxporspanwithfont-semiboldalone is reported.For text styled by hand, a separate script that does not use the rule finds the same 12 and 16, so there are no missed cases. I read all 28 of these reports and found no false positives. The size reports are the same 264 as on the earlier scaffold (
fd4e9cd1), where I read all 84 reports on headings andspanand found no false positives. I did not read each of the 180preports with a size. Other elements and components that set a size (Field.Label,div,Table.Head, ...) are out of scope.Checks
packages/eslint-plugin:test(88 tests, including 46 for the new rule),lint,type-checkandbuildpass. I broke the interpolation check, the role check and the heading weight check one at a time, and the tests failed each time.recommended.test.tschecks thatrecommendeddoes not report the rule, and that a config that enables it does.fmt:checkanddocs:checkpass. Changeset: minor for@tailor-platform/eslint-plugin-app-shell.🤖 Generated with Claude Code