Skip to content

fix(mobile): stop theme-token opacity modifiers from silently dropping - #6344

Open
iscekic wants to merge 1 commit into
mainfrom
kwf/janitor-2026-09-19-experience-and-design-74b8
Open

iscekic wants to merge 1 commit into
mainfrom
kwf/janitor-2026-09-19-experience-and-design-74b8

Conversation

@iscekic

@iscekic iscekic commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Changelog for users

  • A failed permission response now shows a red-tinted error banner instead of one that paints no tint.
  • A failed question submit shows the same red-tinted error banner.
  • An errored attachment that cannot be retried now shows a red-tinted border, so it no longer looks like a healthy chip.

Changelog for maintainers

  • apps/mobile/src/components/agents/permission-card.tsx:113 — accepted: bg-destructive/10 was dropped on the CSS-variable token and painted no tint; now bg-danger-tile-bg.
  • apps/mobile/src/components/agents/question-card.tsx:210 — accepted: the question-submit banner had the same dropped tint; now bg-danger-tile-bg.
  • apps/mobile/src/components/agents/attachment-preview-strip.tsx:386 — accepted: an errored non-retryable chip fell back to border-border; now border-danger-tile-border, while the retryable case keeps full border-destructive.
  • The class check flags a /opacity modifier on the CSS-variable theme tokens (background, foreground, card, popover, primary, secondary, muted, muted-soft, accent, accent-soft, destructive, border, input, ring, ink2, good, warn, info, danger, agent-*) and advises the paired *-tile-bg / *-tile-border token or a concrete colour.
  • The rule leaves non-variable Tailwind colours such as neutral-500/20 alone, and the separate *-black/<alpha> ban is unchanged.
  • The ban is pinned to checkCompilerStillDropsThese(): it fails and the rule can be removed once color-mix over an @theme inline variable stops deferring.
  • Review hint: the check scans both apps/mobile/src and packages/app-shared/src, so violations outside the agent components are caught too.
  • Comment-only reword in pr-review-overview-parts.tsx; no behaviour change.

E2E proof

ux-check: Attachment chips: an errored non-retryable chip's border reads as red-tinted and clearly distinct from a healthy chip's neutral border, while an errored retryable chip still shows the… — e7-attachment-chips-dark.png

[e6] ux-check: Error strip layout: showing and hiding the permission/question error strip does not move, blank, or jump the card body vs. — android emulator-5606: failing the question submit (nextjs faulted down) shows the strip — e6-question-error.digest 'android.widget.TextView Failed to submit answer. Please try again. tappable [76,1588][1004,1625]' — and the card body is not blanked (question 'Which option should I pick?' [77,1683][1005,1729], Skip [68,2173][534,2288], Retry [552,2173][1013,2288]) vs. the error-free run in e6-question-error-free.digest ('SCENE e6-question-error-free OK'); the same strip+body state is captured in dark in e6-question-error-dark.digest ('SCENE e6-question-error-dark OK'), with screenshots…

[e6] ux-check: Error strip layout: showing and hiding the permission/question error strip does not move, blank, or jump the card body vs. — prior/e6-question-error.png

[e6] ux-check: Error strip layout: showing and hiding the permission/question error strip does not move, blank, or jump the card body vs.

[e6] ux-check: Error strip layout: showing and hiding the permission/question error strip does not move, blank, or jump the card body vs. — prior/e6-question-error-dark.png

[e4] ux-check: Failed permission response shows a destructive-tint error strip, light and dark — android emulator-5554: the failed permission response rendered the error strip with copy 'Failed to respond to permission. Please try again.' between the card header and body in light (e4-failed-permission-light.png, e4-failed-permission-light.log) and dark (e4-failed-permission-dark.png, e4-failed-permission-dark.log), with a 'Retry' action; after the injected stall cleared, retry resolved the request ('Show git status in short format tool, completed', 'Working tree is clean.' in e4-recovery.log). Fault injected with fault.sh stall cloudflare-session-ingest --hold 150 (device network verbs…

[e4] ux-check: Failed permission response shows a destructive-tint error strip, light and dark — prior/e4-failed-permission-dark.png

[e4] ux-check: Failed permission response shows a destructive-tint error strip, light and dark

[e4] ux-check: Failed permission response shows a destructive-tint error strip, light and dark — prior/e4-failed-permission-light.png

[e8] ux-check: No false destructive tint on healthy cards and chips — android emulator-5554: healthy permission card (e8-permission-card-light.png, 'Allow Bash?' in e8-permission-card-light.log), healthy question card (e8-question-card.png, 'Agent needs input' in e8-question-card.log) and a healthy non-errored attachment chip (e8-attachment-chip.png, 'e8-attach.png, Uploaded' in e8-attachment-chip.log) all render without error copy; destructive background/border tint is judged by the visual reviewer from these captures.

[e8] ux-check: No false destructive tint on healthy cards and chips — prior/e8-question-card.png

[e8] ux-check: No false destructive tint on healthy cards and chips

[e8] ux-check: No false destructive tint on healthy cards and chips — prior/e8-permission-card-light.png

E2E proof — log excerpts

[e1] ux-check: Failed question submit: the same destructive-tinted error strip r -> pass :: android emulator-5554: after <internal>, Send answers showed TextView "Failed to submit answer. Please try again." and Button "Retry" in Light (e1-failed-submit-light.log; e1-theme-light.log RadioButton "Light" [checked]) and Dark (e1-failed-submit-dark.log; e1-theme-dark.log RadioButton "Dark" [checked]) using kilo/fake-deterministic __fake__:question (e1-stall-nextjs-light.log <internal>); no UX-DEFECT — retryable error kept Continue selected; destructive tint/legibility left to visual reviewer.
/home/igor_kilocode_ai/.local/share/kwf/sections/janitor-2026-09-19-experience-and-design-74b8/e2e-mobile-app/e1-failed-submit-light.log
platform: android emulator-5554
theme: Light (Preferences RadioButton Light checked before this run)
session: ses_f47c83e87ffdHJErklsRr9D6kT
stub: kilo/fake-deterministic __fake__:question:e1c
fault: <internal>
SCENE digest after Send answers:
TextView "Agent needs input"
TextView "The agent is waiting for your answer. While this question is open, your message to the agent is paused."
TextView "Failed to submit answer. Please try again."
TextView "Which environment should we use?"
Button "Continue, Continue the E2E scenario, selected"
Button "Retry"
TextView "Retry"
/home/igor_kilocode_ai/.local/share/kwf/sections/janitor-2026-09-19-experience-and-design-74b8/e2e-mobile-app/e1-theme-light.log
platform: android emulator-5554
Preferences appearance:
RadioButton "Light" [checked]
TextView "Light"
RadioButton "Dark"
TextView "Dark"
/home/igor_kilocode_ai/.local/share/kwf/sections/janitor-2026-09-19-experience-and-design-74b8/e2e-mobile-app/e1-failed-submit-dark.log
platform: android emulator-5554
theme: Dark (Preferences appearance Dark tapped before this run)
session: ses_f47ce305bffewMRStiVFeceOr0
stub: kilo/fake-deterministic __fake__:question:e1b
fault: <internal>
SCENE digest after Send answers:
TextView "Agent needs input"
TextView "The agent is waiting for your answer. While this question is open, your message to the agent is paused."
TextView "Failed to submit answer. Please try again."
TextView "Which environment should we use?"
Button "Continue, Continue the E2E scenario, selected"
Button "Retry"
TextView "Retry"
/home/igor_kilocode_ai/.local/share/kwf/sections/janitor-2026-09-19-experience-and-design-74b8/e2e-mobile-app/e1-theme-dark.log
platform: android emulator-5554
Preferences appearance:
RadioButton "Dark" [checked]
TextView "Dark"
RadioButton "Light"
TextView "Light"
/home/igor_kilocode_ai/.local/share/kwf/sections/janitor-2026-09-19-experience-and-design-74b8/e2e-mobile-app/e1-stall-nextjs-light.log
<internal>[755676] for 90s
<internal>
Owner request

Surface: the mobile app (apps/mobile).

fix(mobile): stop theme-token opacity modifiers from silently dropping

Problem

The repo documents that Tailwind's /opacity modifier does not work on the CSS-variable theme colors. apps/mobile/src/global.css (lines 5-9 and 55-58) says the /opacity modifier 'still does NOT work with these CSS-variable tokens' and ships pre-baked alpha pairs for that reason; apps/mobile/AGENTS.md states 'Opacity modifiers do not work on theme colors (CSS variables): bg-destructive/10 fails.' NativeWind v5 cannot decompose the variables, so the declaration is dropped and the element paints nothing, with no build error or warning. The repo already provides the fix tokens (--danger-tile-bg / --danger-tile-border, mapped to bg-danger-tile-bg / border-danger-tile-border), and tools/nativewind/check-classes.mjs exists to catch 'patterns that go wrong in a way nothing else reports' and runs in .github/workflows/kilo-app-ci.yml, but it has no rule for theme-token opacity, so instances survived and regressed (see commits 0921e4f, 8244809, 3a811fc).

Evidence

  • apps/mobile/src/components/agents/permission-card.tsx:113 - <View className="border-b border-border bg-destructive/10 px-4 py-2">: the permission-error banner renders with no destructive tint.
  • apps/mobile/src/components/agents/question-card.tsx:210 - the same bg-destructive/10 banner for question-submit errors.
  • apps/mobile/src/components/agents/attachment-preview-strip.tsx:386 - isErrored && !description.showRetry && 'border-destructive/60': an errored, non-retryable attachment chip falls back to border-border and looks like a healthy chip.
  • Working pattern to copy: apps/mobile/src/components/agents/message-error-boundary.tsx:98 uses rounded-lg border border-danger-tile-border bg-danger-tile-bg px-3 py-2.

Requested behavior

  1. Add a rule to tools/nativewind/check-classes.mjs that flags a /opacity modifier on the CSS-variable theme tokens (background, foreground, card, popover, primary, secondary, muted, accent, destructive, border, input, ring, ink2, muted-soft, accent-soft, good, warn, info, danger, agent-*), with advice to use the paired *-tile-bg / *-tile-border token or a concrete color. Do not flag non-variable Tailwind colors (e.g. neutral-500/20) or the already-banned *-black/<alpha>. Follow the file's convention and pin the new ban to a checkCompilerStillDropsThese() assertion so it lifts when react-native-css stops dropping the declaration.
  2. Replace bg-destructive/10 with bg-danger-tile-bg in permission-card.tsx:113 and question-card.tsx:210.
  3. Replace border-destructive/60 with border-danger-tile-border in attachment-preview-strip.tsx:386 (keeps the softer tinted border; the retryable case already uses full border-destructive).

Scope and exclusions

Acceptance checks

  • pnpm --filter kilo-app run check:classes passes from the repo root (the new rule finds no remaining instance).
  • rg -n '(bg|text|border|ring|fill|stroke)-(background|foreground|card|popover|primary|secondary|muted|accent|destructive|border|input|ring|good|warn|info|danger|agent-[a-z]+)(-foreground)?/[0-9]+' apps/mobile/src returns no matches.
  • From apps/mobile/: pnpm format && pnpm typecheck && pnpm lint && pnpm check:unused and pnpm --filter kilo-app test all pass.
  • Local end-to-end proof through the normal mobile workflow: trigger a failed permission response, a failed question submit, and a failed attachment upload; capture a screenshot showing the destructive tint on each in both light and dark themes, plus the decisive sanitized log line for each. The PR description must include those screenshots and log excerpts.

[e3] Terminal-error attachment chip renders the tinted destructive border in light and dark themes — android/emulator-5554; the harvested replay (files/replay-e3.json, state session-open) now lands on the committed terminal-error chip and both runs report 'SCENE e3 OK' with digest line 'android.widget.Button report.pdf, This file can't be uploaded.' in e3-scene-light.log and e3-scene-dark.log (dark via KILO_E2E_NIGHT=yes), captures e3-terminal-chip-light.png / e3-terminal-chip-dark.png; the chip's tint colour is the visual reviewer's call, and the plan's demanded 'sanitized log line for the rejected upload' does not exist because the plan itself states there is no live trigger (the picker…

[e3] Terminal-error attachment chip renders the tinted destructive border in light and dark themes — e3-terminal-chip-light.png

[e3] Terminal-error attachment chip renders the tinted destructive border in light and dark themes

[e3] Terminal-error attachment chip renders the tinted destructive border in light and dark themes — e3-terminal-chip-dark.png

[e6] ux-check: Error strip layout: showing and hiding the permission/question error strip does not move, blank, or jump the card body vs. — android emulator-5606: failing the question submit (nextjs faulted down) shows the strip — e6-question-error.digest 'android.widget.TextView Failed to submit answer. Please try again. tappable [76,1588][1004,1625]' — and the card body is not blanked (question 'Which option should I pick?' [77,1683][1005,1729], Skip [68,2173][534,2288], Retry [552,2173][1013,2288]) vs. the error-free run in e6-question-error-free.digest ('SCENE e6-question-error-free OK'); the same strip+body state is captured in dark in e6-question-error-dark.digest ('SCENE e6-question-error-dark OK'), with screenshots…

[e6] ux-check: Error strip layout: showing and hiding the permission/question error strip does not move, blank, or jump the card body vs. — e6-question-error-free.png

[e6] ux-check: Error strip layout: showing and hiding the permission/question error strip does not move, blank, or jump the card body vs.

[e6] ux-check: Error strip layout: showing and hiding the permission/question error strip does not move, blank, or jump the card body vs. — e6-question-error-free-dark.png

[e7] ux-check: Attachment chips: an errored non-retryable chip's border reads as red-tinted and clearly distinct from a healthy chip's neutral border, while an errored retryable chip still shows the… — android/emulator-5554; the same composer shows the three branches distinctly in e7-chips.log — 'android.widget.Button report.pdf, This file can't be uploaded.' (terminal, non-retryable: no retry control, only Remove), 'android.widget.Button Retry uploading diagram.png' (errored retryable: retry control present) and 'android.widget.Button notes.txt, Uploaded' (healthy) — so each state is labelled, gives a next action (Retry / Remove / open), and shows no missing feedback, lost work or broken recovery: no UX-DEFECT found; the light-vs-dark red-tint comparison itself is a pixel claim that…

[e7] ux-check: Attachment chips: an errored non-retryable chip's border reads as red-tinted and clearly distinct from a healthy chip's neutral border, while an errored retryable chip still shows the… — e7-attachment-chips-light.png

Follow-ups (not changed here)

e8-attachment-chip

e1-failed-submit-dark-fresh

e1-failed-submit-light2

Surface: the mobile app (apps/mobile).

fix(mobile): stop theme-token opacity modifiers from silently dropping

## Problem
The repo documents that Tailwind's `/opacity` modifier does not work on the CSS-variable theme colors. `apps/mobile/src/global.css` (lines 5-9 and 55-58) says the `/opacity` modifier 'still does NOT work with these CSS-variable tokens' and ships pre-baked alpha pairs for that reason; `apps/mobile/AGENTS.md` states 'Opacity modifiers do not work on theme colors (CSS variables): `bg-destructive/10` fails.' NativeWind v5 cannot decompose the variables, so the declaration is dropped and the element paints nothing, with no build error or warning. The repo already provides the fix tokens (`--danger-tile-bg` / `--danger-tile-border`, mapped to `bg-danger-tile-bg` / `border-danger-tile-border`), and `tools/nativewind/check-classes.mjs` exists to catch 'patterns that go wrong in a way nothing else reports' and runs in `.github/workflows/kilo-app-ci.yml`, but it has no rule for theme-token opacity, so instances survived and regressed (see commits 0921e4f, 8244809, 3a811fc).

## Evidence
- `apps/mobile/src/components/agents/permission-card.tsx:113` - `<View className="border-b border-border bg-destructive/10 px-4 py-2">`: the permission-error banner renders with no destructive tint.
- `apps/mobile/src/components/agents/question-card.tsx:210` - the same `bg-destructive/10` banner for question-submit errors.
- `apps/mobile/src/components/agents/attachment-previe
@iscekic iscekic added the kwf-janitor Nightly mobile janitor request label Sep 19, 2026
Comment thread tools/nativewind/check-classes.mjs
@kilo-code-bot

kilo-code-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The three bg-destructive/10bg-danger-tile-bg and border-destructive/60border-danger-tile-border fixes are correct, but the new checkCompilerStillDropsThese() pin in tools/nativewind/check-classes.mjs compiles with default inlineVariables, unlike the app's Metro config, so it may fail CI or validate the wrong path.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
tools/nativewind/check-classes.mjs 247 New compiler assertion calls compile(css) without { inlineVariables: false }, unlike apps/mobile/metro.config.js:56 and button.mounted.test.tsx:70; with variables inlined the color-mix resolves and themeOpacity will not contain colorMix, so the pin can trip check:classes in CI.
Files Reviewed (5 files)
  • apps/mobile/src/components/agents/attachment-preview-strip.tsx - no issues
  • apps/mobile/src/components/agents/permission-card.tsx - no issues
  • apps/mobile/src/components/agents/question-card.tsx - no issues
  • apps/mobile/src/components/pr-review/pr-review-overview-parts.tsx - no issues (comment-only)
  • tools/nativewind/check-classes.mjs - 1 issue

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic marked this pull request as draft September 19, 2026 06:09
@iscekic
iscekic marked this pull request as ready for review September 19, 2026 06:37
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 19, 2026
@iscekic iscekic self-assigned this Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review. kwf-janitor Nightly mobile janitor request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant