Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ function AttachmentChip({
'overflow-hidden rounded-md border border-border bg-card',
isImage ? 'h-16 w-20' : 'h-12 w-48',
description.showRetry && 'border-destructive',
isErrored && !description.showRetry && 'border-destructive/60'
isErrored && !description.showRetry && 'border-danger-tile-border'
)}
>
{description.showRetry ? (
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/components/agents/permission-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function PermissionCard({
</View>

{presentation.errorMessage ? (
<View className="border-b border-border bg-destructive/10 px-4 py-2">
<View className="border-b border-border bg-danger-tile-bg px-4 py-2">
<Text className="text-xs text-destructive">{presentation.errorMessage}</Text>
</View>
) : null}
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/components/agents/question-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export function QuestionCard({
</View>

{presentation.errorMessage ? (
<View className="border-b border-border bg-destructive/10 px-4 py-2">
<View className="border-b border-border bg-danger-tile-bg px-4 py-2">
<Text className="text-xs text-destructive">{presentation.errorMessage}</Text>
</View>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ export function describePrState(args: {
return { labelKey: 'prReview.overview.stateOpen', tone: 'muted', icon: GitPullRequest };
}

// Theme colors are CSS variables Tailwind opacity modifiers like
// `bg-good/10` don't work on them. The chip uses a flat muted background
// and lets the foreground color carry the tone so it stays legible in
// both themes without needing per-tone backgrounds.
// Theme colors are CSS variables, so Tailwind opacity modifiers on them are
// silently dropped. The chip uses a flat muted background and lets the
// foreground color carry the tone so it stays legible in both themes
// without needing per-tone backgrounds.
const TONE_FG_CLASS = {
good: 'text-good',
warn: 'text-warn',
Expand Down
28 changes: 28 additions & 0 deletions tools/nativewind/check-classes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ const RULES = [
pattern: new RegExp(`\\b(?:${COLOR_UTILITIES})-black/\\d+\\b`, 'g'),
advice: 'compiles to an unparseable #NaN colour; use a concrete value, e.g. bg-[#00000066]',
},
{
// A `/opacity` modifier on a theme colour compiles to
// `color-mix(in oklab, var(--color-…) n%, transparent)`. NativeWind v5
// cannot decompose the variable, so the runtime drops the declaration and
// the element paints nothing, with no build error. The `*-tile-bg` /
// `*-tile-border` pairs pre-bake the alpha instead.
pattern: new RegExp(
`\\b(?:${COLOR_UTILITIES})-(?:background|foreground|card|popover|primary|secondary|muted-soft|muted|accent-soft|accent|destructive|border|input|ring|ink2|good|warn|info|danger|agent-[a-z]+)(?:-foreground)?/\\d+\\b`,
'g'
),
advice:
'the /opacity modifier is dropped on CSS-variable theme colours; use the paired *-tile-bg / *-tile-border token or a concrete colour',
},
{
// `text-align` only accepts auto/left/right/center/justify, so the
// logical keywords are dropped and the text keeps its default alignment.
Expand Down Expand Up @@ -226,6 +239,21 @@ function checkCompilerStillDropsThese() {
if (!JSON.stringify(alignment).includes('text-align')) {
fail('check-classes: text-align: start is no longer dropped — drop the text-start/end rule');
}

// A `/opacity` modifier on an `@theme inline` variable is emitted as a
// deferred colorMix over the variable; the runtime cannot decompose it and
// drops the declaration. When the compiler resolves it instead, this stops
// matching and the rule above can go.
const themeOpacity = firstValue(
Comment thread
iscekic marked this conversation as resolved.
'@theme inline { --color-destructive: var(--destructive); }\n' +
':root { --destructive: #b0483a; }\n' +
'.x { background-color: color-mix(in oklab, var(--color-destructive) 10%, transparent); }'
);
if (!themeOpacity.includes('colorMix')) {
fail(
`check-classes: color-mix over an @theme inline variable now compiles to ${themeOpacity} instead of a deferred colorMix — drop the theme-token opacity rule`
);
}
}

checkCompilerStillDropsThese();
Expand Down
Loading