FUI1009: Component caller styling is unresolved
moderateA governed component received styling the check could not read completely.
Details
{
"code": "FUI1009",
"ruleId": "components/unresolved-style",
"category": "Canonical Usage",
"defaultSeverity": "moderate",
"lifecycle": "experimental",
"fixAvailable": false,
"evidenceRequired": true
}Guidance
## What it means
This component has a `style.ownership` record, so the check must read every style input that reaches it. Here it could not finish: the `className` or `style` expression, the wrapper it passes through, or the stylesheet it names could not be resolved to a complete set of CSS properties.
The finding carries the reason code. Common ones:
- `dynamic-expression` — a value the check cannot read, such as a class name built by concatenation. - `unknown-helper` — a call whose implementation is not a supported class helper. - `unknown-utility` — a class with no known property effect in this project's context. - `unresolved-forwarding` — a wrapper whose `className` route to the component is not proven. - `unsupported-selector` / `unsupported-composition` — a CSS Module rule that is not a plain class-local declaration. - `unknown-property` — a property name outside the appearance vocabulary. - `acquisition-failed` / `analysis-limit` — an input could not be read, or a bound limit stopped the walk.
FUI1009 is advisory: it reports a gap in evidence rather than a proven violation. Its severity comes from the component's ownership record.
## Why it matters
The check will not claim a component is styled correctly when it could not see the styling. An unreadable class expression is the shape most likely to carry exactly the override the component's props are supposed to own, and it is invisible in review.
The gap is also recorded as incomplete coverage for the region, so the run reports what it could not read instead of reporting a clean pass.
## Before
The class name is assembled at runtime, so nothing can be read from it:
```tsx import { Button } from "@/components/ui/button";
export function SaveAction({ tone }: { tone: string }) { return <Button className={`bg-${tone}`}>Save changes</Button>; } ```
## Repair
Move the decision into the component's approved props:
```tsx import { Button } from "@/components/ui/button";
export function SaveAction({ active }: { active: boolean }) { return <Button variant={active ? "primary" : "secondary"}>Save changes</Button>; } ```
When the class really belongs at the call site, write complete class strings in each branch. Whole strings are readable; fragments joined to a variable are not:
```tsx <Button className={active ? "mt-4" : "mt-2"}>Save changes</Button> ```
Class helpers are read when they are the real ones: `clsx`, the default export of `classnames`, `twMerge` from `tailwind-merge`, and `cva` from `class-variance-authority`. A local `cn` is read when its body simply forwards its arguments to one of those. A helper imported from somewhere else, or reassigned, stays unresolved.
For CSS Modules, a class bound to a specific `*.module.css` import resolves when the rule is a plain class-local declaration — `.root { … }` or `.root:hover { … }`. Descendant selectors, `:global`, `composes` and computed keys report unresolved instead of attaching the stylesheet to the component.
For a wrapper, keep the `className` route visible: destructure it, or spread props that carry it, and pass it to the governed component rather than dropping or overwriting it.
## Do not suppress this one
A suppression hides the finding; it does not restore the evidence. Coverage is recomputed from the source facts on every run, so the region still reports incomplete analysis and the run still cannot claim a clean result. Fix the expression, or accept the reported gap knowingly.
## Verify the change
Run `npx @usefragments/cli check --changed --format agent` and confirm FUI1009 is gone. Then run `npx @usefragments/cli check --changed --ci` and confirm the analysis reports no incomplete region 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.