Raised across the review rounds of LibreChat-AI#16487, which added the disabledStyle: 'fill' appearance and three shared recipes (disabledFillClasses, disabledInkClasses, disabledWithinFillClasses, plus peerDisabledInkClasses).
Each round found one more shared control whose disabled look bypasses the recipes: some dim through a disabled: utility, others through a JS branch (disabled ? 'opacity-50' : '' in Radio, SecretInput), Ariakit's aria-disabled (DropdownPopup), a has-[:disabled] group (InputOTP) or a wrapper that is never disabled itself (InputNumber). LibreChat-AI#16487 covers every shared primitive it found except Button and OGDialogTemplate (owned by LibreChat-AI#15684), but nothing stops the next component from dimming without a recipe, and feature code under client/src was never in scope.
Gap: make the rule mechanical. A design-lint rule (next to the @shadcn/lint rules in eslint.config.mjs) that reports a class list carrying a disabled dim (disabled:opacity-*, aria-disabled:opacity-*, data-[disabled]:opacity-*, has-[:disabled]:opacity-*, or a bare opacity-* inside a disabled conditional) without one of the recipes, with the existing violations recorded in eslint-suppressions.json as a backlog. Then burn down the feature-code backlog in batches.
Raised across the review rounds of LibreChat-AI#16487, which added the
disabledStyle: 'fill'appearance and three shared recipes (disabledFillClasses,disabledInkClasses,disabledWithinFillClasses, pluspeerDisabledInkClasses).Each round found one more shared control whose disabled look bypasses the recipes: some dim through a
disabled:utility, others through a JS branch (disabled ? 'opacity-50' : ''inRadio,SecretInput), Ariakit'saria-disabled(DropdownPopup), ahas-[:disabled]group (InputOTP) or a wrapper that is never disabled itself (InputNumber). LibreChat-AI#16487 covers every shared primitive it found exceptButtonandOGDialogTemplate(owned by LibreChat-AI#15684), but nothing stops the next component from dimming without a recipe, and feature code underclient/srcwas never in scope.Gap: make the rule mechanical. A design-lint rule (next to the
@shadcn/lintrules ineslint.config.mjs) that reports a class list carrying a disabled dim (disabled:opacity-*,aria-disabled:opacity-*,data-[disabled]:opacity-*,has-[:disabled]:opacity-*, or a bareopacity-*inside adisabledconditional) without one of the recipes, with the existing violations recorded ineslint-suppressions.jsonas a backlog. Then burn down the feature-code backlog in batches.