FUI2021: Class expression must be static
moderateA class expression could not be read, so the classes it produces were never checked.
Details
{
"code": "FUI2021",
"ruleId": "styles/require-static-classes",
"category": "Tokens",
"defaultSeverity": "moderate",
"lifecycle": "experimental",
"fixAvailable": false,
"evidenceRequired": true
}Guidance
## What it means
A `className` expression produced no readable class list. The check recorded the expression and then found no resolution for it, so every class it might produce went unchecked. The finding quotes the reason it stopped, such as a computed value or an unrecognised helper.
This rule is off by default. It fires only after your project turns it on:
```ts govern: { rules: { 'styles/require-static-classes': { enabled: true, severity: 'warn' }, }, }, ```
Enabling `tailwind/unknown-class` requires the same evidence, so class validity checking reports these gaps as well.
## Why it matters
Every other class rule — palette, scale, unknown utility, arbitrary value — reads a class list. An expression nothing can read is a hole in all of them at once, and it is silent: the run looks clean because nothing was examined.
## Before
The class is assembled from a variable, so no complete class name exists in the source:
```tsx export function Status({ tone }: { tone: string }) { return <span className={"bg-" + tone}>Ready</span>; } ```
## Repair
Write complete class strings in each branch. A conditional, a logical expression, an array, an object of class keys and a template literal with whole classes around the interpolation are all readable:
```tsx export function Status({ active }: { active: boolean }) { return <span className={active ? "bg-accent" : "bg-muted"}>Ready</span>; } ```
Use a real helper rather than string arithmetic. `clsx`, the default export of `classnames`, `twMerge` from `tailwind-merge` and `cva` from `class-variance-authority` are read directly, and a local `cn` is read when its body forwards its arguments to one of them:
```tsx import clsx from "clsx";
export function Status({ active }: { active: boolean }) { return <span className={clsx("rounded-md", active && "bg-accent")}>Ready</span>; } ```
With `cva`, keep the variant table, its defaults and the selected values in source: the declared branches are read, and an unknown selection keeps the union of the reachable ones.
## A suppression is not a repair
Coverage is recomputed from the source facts on every run. Suppressing FUI2021 removes the line from the report and leaves the class expression exactly as unreadable as before, so the region still reports incomplete analysis. Make the expression readable, or accept the reported gap knowingly.
## Verify the change
Run `npx @usefragments/cli check --changed --format agent` and confirm FUI2021 is absent. Then run `npx @usefragments/cli check --changed --ci` and confirm no region reports incomplete class analysis for those files.
Intentional exception? Use a narrow, reasoned directive from the in-source exception reference.
Next steps
- RulesThe rules that emit FUI codes.
- ExceptionsIn-source allows and Cloud exceptions.