Repository navigation
Mechanize UI color token enforcement check - #614
ArthurArthurArthur0817 wants to merge 2 commits into
Conversation
5b0898d to
64cdc76
Compare
64cdc76 to
218ebe2
Compare
218ebe2 to
f5ea34c
Compare
cc08714 to
f5ea34c
Compare
There was a problem hiding this comment.
2 issues found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/test_check_css_tokens.py">
<violation number="1" location="tests/test_check_css_tokens.py:29">
P1: `test_parser_regressions` always fails because the checker reports two literals in this fixture while the assertion expects zero. Make the fixture use non-literal values (or update the expected count if these are intended violations) so the required `css-tokens-tests` gate can pass.</violation>
</file>
<file name="scripts/check-css-tokens.py">
<violation number="1" location="scripts/check-css-tokens.py:1">
P3: Several added lines contain trailing spaces, which makes the patch fail `git diff --check` and the repository’s formatting gate. Remove the trailing whitespace from the new script.</violation>
</file>
Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.
Tip: Review your code locally with the cubic CLI to iterate faster.
View guided diff | Re-trigger cubic
| } | ||
| .card { color: var(--brand-violet); } | ||
| """, encoding="utf-8") | ||
| self.assertEqual(check_css_tokens.check_css_file(self.test_file), 0) |
There was a problem hiding this comment.
P1: test_parser_regressions always fails because the checker reports two literals in this fixture while the assertion expects zero. Make the fixture use non-literal values (or update the expected count if these are intended violations) so the required css-tokens-tests gate can pass.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/test_check_css_tokens.py, line 29:
<comment>`test_parser_regressions` always fails because the checker reports two literals in this fixture while the assertion expects zero. Make the fixture use non-literal values (or update the expected count if these are intended violations) so the required `css-tokens-tests` gate can pass.</comment>
<file context>
@@ -0,0 +1,70 @@
+ }
+ .card { color: var(--brand-violet); }
+ """, encoding="utf-8")
+ self.assertEqual(check_css_tokens.check_css_file(self.test_file), 0)
+
+ def test_invalid_standard_properties(self):
</file context>
| @@ -0,0 +1,45 @@ | |||
| #!/usr/bin/env python3 | |||
There was a problem hiding this comment.
P3: Several added lines contain trailing spaces, which makes the patch fail git diff --check and the repository’s formatting gate. Remove the trailing whitespace from the new script.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/check-css-tokens.py:
<comment>Several added lines contain trailing spaces, which makes the patch fail `git diff --check` and the repository’s formatting gate. Remove the trailing whitespace from the new script.</comment>
<file context>
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+"""Enforces that CSS color literals are only used as custom property values."""
+
+import sys
+import re
+from pathlib import Path
+
+COMMENT_RE = re.compile(r'/\*.*?\*/', re.DOTALL)
+
</file context>
| gate audit-tests unittest_gate "$ROOT/tests/test_audit.py" | ||
| gate rust-style-tests unittest_gate "$ROOT/tests/test_check_rust_style.py" | ||
| gate css-tokens-tests unittest_gate "$ROOT/tests/test_check_css_tokens.py" | ||
| gate css-tokens "$PYTHON" "$ROOT/scripts/check-css-tokens.py" |
There was a problem hiding this comment.
This gate fails at this head on the PR's own web/styles.css: the checker reports @media (prefers-color-scheme: light) and @media (prefers-color-scheme: dark) as violations. DECLARATION_RE in scripts/check-css-tokens.py reads prefers-color-scheme: light) { .editor-stack:not([data-theme="dark"]) { --code-bg: #ffffff as one declaration named prefers-color-scheme, so the first token in each block is flagged, and a selector with a pseudo-class does the same (.card:hover { --card-color: #fff; } is reported). Match declarations only inside a rule's braces, after its selector or at-rule prelude, and add both shapes to tests/test_check_css_tokens.py.
cec74c5 to
1d9e224
Compare
Extracted hardcoded color literals into semantic custom properties inside the :root scope to ensure consistency and prepare for strict token enforcement. Change-Id: If3c8d4de6dd43e23ef2888c86de2f323e54f3b8e
1d9e224 to
21664f0
Compare
6bffcf3 to
28b9d54
Compare
28b9d54 to
9d8c03d
Compare
| gate audit-tests unittest_gate "$ROOT/tests/test_audit.py" | ||
| gate rust-style-tests unittest_gate "$ROOT/tests/test_check_rust_style.py" | ||
| gate css-tokens-tests unittest_gate "$ROOT/tests/test_check_css_tokens.py" | ||
| gate css-tokens "$PYTHON" "$ROOT/scripts/check-css-tokens.py" |
There was a problem hiding this comment.
This gate fails only through main() calling sys.exit(1), and no test drives main(): replacing that sys.exit(1) with pass leaves all six cases in tests/test_check_css_tokens.py passing, and the gate still exits 0 on today's stylesheets, so a regression there would turn the gate into one that can never fail. Add a case that points the scan at a temporary tree (for example by patching check_css_tokens.ROOT to a directory holding web/sub/x.css with color: #f00;) and asserts SystemExit with code 1, which also pins that web/ is walked recursively.
9d8c03d to
6ab2629
Compare
6ab2629 to
3f0c14c
Compare
3f0c14c to
03923ec
Compare
Added a strict mechanical checker that scans CSS files for hardcoded color literals. Excludes custom property definitions and is integrated into test.sh alongside unit tests. Change-Id: I0bd897637b2ff51231646c5df6acfb107f315354
03923ec to
e8e68bd
Compare
| # Match hardcoded color literals: #hex, or functions | ||
| COLOR_RE = re.compile( | ||
| r"(#[0-9a-fA-F]{3,8}\b|" | ||
| r"\b(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb|color)\([^)]+\))", |
There was a problem hiding this comment.
Named colors pass this pattern, so the three color: white rules on .pill-button.danger, .run-button and .final-code button in web/styles.css stay hardcoded, even though --on-accent exists for exactly that text (white on --accent-fill or --danger). The .turn-ring mask in this PR also passes only because #000 became black. Switch those three rules to var(--on-accent) here. As a follow-up, add the named colors (keeping transparent and currentColor allowed) to COLOR_RE and exempt mask and -webkit-mask, where only alpha matters.
| text = COMMENT_RE.sub( | ||
| lambda m: "\n" * m.group().count("\n"), path.read_text(encoding="utf-8") | ||
| ) |
There was a problem hiding this comment.
Strings and url() are only removed per declaration, after DECLARATION_RE has already split at ; and }. content: "a; b: #fff"; therefore reports a violation on a property named b, and so would any url("data:image/svg+xml;utf8,...") carrying a fill='#fff'. QUOTED_RE also stops at an escaped quote ("\"#fff") and misses URL(. Blank them across the whole text before splitting, keeping newlines so the line count holds, then call COLOR_RE.search(val) directly, make QUOTED_RE r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|url\([^)]*\)' with re.IGNORECASE, and add a test with a ; inside content.
| text = COMMENT_RE.sub( | |
| lambda m: "\n" * m.group().count("\n"), path.read_text(encoding="utf-8") | |
| ) | |
| text = COMMENT_RE.sub( | |
| lambda m: "\n" * m.group().count("\n"), path.read_text(encoding="utf-8") | |
| ) | |
| text = QUOTED_RE.sub(lambda m: "\n" * m.group().count("\n"), text) |
|
Some review comments on this pull request are not settled yet. Please refine the change per the review comments listed below, reply on each thread with what you changed or why you did not, and mark each thread resolved yourself once it is settled (Resolve conversation, at the bottom of the thread). The pull request stays blocked while review threads are open. Not addressed at the current head:
Addressed at the current head; reply with what changed and resolve:
Checked at e8e68bd. |
Problem and behavior
UI colors must be defined as design tokens in
:rootto ensure consistency across light and dark themes. However, this rule was not mechanized, allowing hardcoded color literals (Hex/RGB/HSL) to slip intoweb/styles.css,web/task.css, andweb/recording/recording.css.This PR converts all existing out-of-scope literals into semantic variables within
:rootand adds a strict mechanical checker (scripts/check-css-tokens.py) to prevent future violations.Notes
The checker script parses scopes like
[data-theme="light"]and@media (prefers-color-scheme: light)identically to:root, recognizing them as valid token definition blocks where color literals are permitted. Standard CSS keywords (likeblack) andvar(...)usages are ignored.Instead of creating an allowlist for the existing technical debt, all 17 prior violations were resolved in the same change. Literals like
#d7f7e5on.agent-pill.ready,#1e1e1eon.editor-panel, and hardcodedmaskorbox-shadowvalues were extracted into new semantic tokens. This allows the checker to operate with a strict zero-tolerance baseline.Testing
tests/scripts/test_check_css_tokens.pyadds coverage for valid token definitions, permitted comments, and exact interception of prohibited literal formats. Integrated the checker intoscripts/test.shascss_tokens_gate. The tests fail if a hardcoded literal is introduced outside a definition block../scripts/test.shpasses; cargo-audit, cargo-deny, actionlint, and GitHub CLI-dependent tests were skipped locally or deferred to CI.Close #578
Summary by cubic
Mechanizes UI color definitions so hardcoded Hex/RGB/HSL literals can't reappear in the CSS files under
web/.Adds
scripts/check-css-tokens.pywith unit tests intests/test_check_css_tokens.py, wired intoscripts/test.shascss-tokens-testsandcss-tokensgates. Replaces existing literals inweb/styles.css,web/task.css, andweb/recording/recording.csswith semantic tokens in:root, except two.turn-ringmask gradients inweb/styles.cssthat now use theblackkeyword. Also swaps the cube's hardcoded#11110fstroke and fill inweb/cube-view.jsforvar(--cube-ink).Closes #578.
Written for commit e8e68bd. Summary will update on new commits.