Skip to content

feat: add design group (gradient text, purple-blue gradient, violet accent, violet hex, shadcn violet theme) - #103

Merged
mgiovani merged 24 commits into
mainfrom
feat/design-style-layer
Oct 14, 2026
Merged

mgiovani merged 24 commits into
mainfrom
feat/design-style-layer

Conversation

@mgiovani

@mgiovani mgiovani commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds a style layer so rules can read CSS, plus the first three rules of the new design group. Closes #86 and #87. Part of #85 and epic #98.

  • Lang::Css covers .css and .scss, parsed with tree-sitter-css. It joins neither CODE_LANGS nor PROSE_LANGS, so no existing rule starts running on stylesheets.
  • StyleDoc (src/style.rs) is built once per file and exposed as ctx.style. It holds CSS declarations with absolute byte spans and Tailwind class tokens grouped per class attribute. Sources:
    • CSS/SCSS: declarations from the tree.
    • HTML: <style> bodies, re-parsed through tree-sitter set_included_ranges so offsets stay absolute, plus style= and class= attributes.
    • TSX: className/class strings (including cn(...) arguments) and style={{...}} objects, with camelCase keys mapped to kebab-case.
  • src/color.rs: pure color parsing (hex, rgb/rgba, hsl/hsla, named colors) and HSL conversion.
  • Gate: StyleDoc is built only when a design-group rule is enabled for the file's lang. Built unconditionally, it tripled HTML lint time. With the gate, the default run is unchanged: 231 ms before vs 229 ms after, on 300 Design2Code pages.

Rules (design group; SLOP052, SLOP053, SLOP055 and SLOP056 Tier B on, SLOP054 Tier C off)

Code Detects
SLOP052 text filled with a gradient (background-clip: text plus a gradient, or bg-clip-text plus a gradient utility)
SLOP053 purple-to-blue gradients: CSS stops in the violet/indigo hue band, Tailwind from-purple/violet/indigo with a matching via/to, bracketed stops
SLOP054 hand-copied Tailwind violet hexes outside gradients and outside custom-property definitions
SLOP055 violet Tailwind accent utilities (text, bg, border, ring, ... with purple, violet or indigo 400-700, variants and opacity allowed) in class lists; flags the look, not the author
SLOP056 shadcn violet theme preset: --primary, --ring, --accent, --sidebar-primary, --chart-1 set to a violet HSL triplet, oklch(), hex or rgb in a block that also defines at least two shadcn marker tokens (--card, --popover, --primary-foreground, ...); a lone brand --primary and zinc, slate or blue presets stay quiet

The five rules own disjoint spans (clip, gradient, bare hex, accent utility, theme token), and none overlaps SLOP009, SLOP018, SLOP021 or SLOP044.

First measurement (superseded)

WebSight's 2023-era AI pages contain none of these tells (0 hits for every rule), so they couldn't show separation. The app-builder cells in Tier decision below replace them as the AI split. The WebSight rows stay in bench/candidates.toml.

Dependency

tree-sitter-css = "0.25" (MIT). Regex cannot track nested blocks, strings and comments in CSS, and tree-sitter is the parser family the crate already uses. cargo tree -i tree-sitter shows a single core (0.26.10). Cargo.lock gains only tree-sitter-css; cc and tree-sitter-language were already locked.

Tier decision

Scored on pinned public repos from four AI app builders (64 repos, 1,455 files; tsx, css, html under src/, shadcn components/ui/ and *.min.* excluded) against verified human files (Ant Design TSX, 11 named-maintainer Tailwind React apps pinned to late 2022 with their CSS, three pinned CSS repos; 1,275 files). Builder provenance markers: Lovable gpt-engineer-app[bot] commits (28 repos, 2026-06 to 2026-10), bolt.new .bolt/config.json (12, 2024-10 to 2026-03), Same.new .same/todos.md (12, 2025-05 to 2026-09), Replit Agent Replit-Commit-Author: Agent trailer (12, 2025-06 to 2026-10). No pre-2020 Tailwind baseline exists.

Bar: AI rate at least 3x the human rate, human rate at most 1% (2% AI floor as guidance only). Re-scored after the second review pass: SLOP053 now requires stops on both the blue side and the purple core (alpha at least 0.5) and pairs stops only within a variant; the SLOP056 marker and violet-floor changes moved no corpus count.

Rule bolt.new Lovable Same.new Replit Agent AI pooled Human verified Tailwind 2022 alone Design2Code
SLOP052 13/526 6/558 4/183 3/188 26/1455 (1.79%) 1/1275 (0.08%) 1/589 3/484
SLOP053 31/526 1/558 19/183 14/188 65/1455 (4.47%) 1/1275 (0.08%) 0/589 0/484
SLOP054 1/526 2/558 0/183 0/188 3/1455 (0.21%) 0/1275 0/589 1/484
SLOP055 (tsx, html) 100/509 13/513 23/171 16/165 152/1358 (11.19%) 6/1000 (0.60%) 6/500 (1.20%) 0/484
SLOP056 (css, html) 1/27 5/58 0/15 1/29 7/129 (5.43%) 0/275 0/89 0/484
  • SLOP052: Tier B on. Under the 2% guidance floor (1.79%) but 22x the human rate.
  • SLOP053: Tier B on.
  • SLOP055: Tier B on. A product call: it flags the look, not the author; humans on Tailwind reach for these shades at 1.2% in the 2022 baseline.
  • SLOP056: Tier B on. Requires two shadcn marker tokens (--card, --primary-foreground, --radius, ...) in the block, so a lone --primary: #9146ff stays quiet. Small denominators: most builder output keeps its theme in one index.css.
  • SLOP054: AI rate 0.21%. Stays Tier C. A violet hue-band widening was measured and rejected: syntax-highlighting themes (codemirror 38%, highlight.js 33%) and Tailwind apps push the human rate far above 1%.

Tailwind v3/v4. SLOP052, SLOP053 and SLOP055 read bg-gradient-to-*, bg-linear-*, bg-radial, bg-conic, the ! important marker before (v3) or after (v4) a utility, the v4 tw: prefix, bracketed stops including oklch() (underscores as spaces, color: hints, /opacity), and ignore the v4 bg-(--brand) shorthand. color.rs parses oklch()/oklab() (hue in deg, grad, rad, turn or none) and shadcn bare HSL triplets. Ceiling: a v3 tw- config prefix is not read.

Rows and per-rule notes are in bench/candidates.toml.

Notes

  • tests/integration.rs: a data-only "css" entry in LANG_DIRS, the same precedent as "html".
  • LintContext.style adds style: None to every test literal. This is one mechanical commit.
  • .css and .scss files now enter the walk, so existing custom rules can match them. CHANGELOG recommends excluding *.min.css.
  • Known ceilings, documented: SCSS $vars and @include can hide a neighboring declaration, and /* ai-slop-ignore */ inside an HTML <style> block is not honored (use an HTML comment around the element). // suppression works in .scss.

Verification

  • just check passes. The lib has 1066 tests, plus the integration, natlang and image harnesses.
  • cargo install --path . followed by stopslop .: zero findings (the repo config selects ALL).
  • With --select design on the fixtures, only the slop_design.* files fire.
  • Reviewed with an 8-lane review (correctness, performance, style, tests, errors, simplicity, architecture, deps). No Critical or Major findings survived verification. The Minor and Nit fixes are in the last four commits.

The CSS re-parse of every <style> body roughly tripled the time to lint 300 real HTML pages (287 ms to 842 ms); with every style rule off by default the default run now matches the base binary (231 ms vs 229 ms).

docs: README rule rows and Style linting section, CHANGELOG, AGENTS.md architecture bullets
Named colors no longer match inside var(), url() or identifiers; SLOP054 skips every custom-property definition; SLOP053 reads from-[#hex] stops; style values report correct columns for template strings and spaced attributes; line_col is memoized. Drops the unused contrast helpers and the ClassList wrapper, and adds check-level tests for all three rules.
wants_style keys on rules declaring STYLE_LANGS and checks the file lang first; a .scss comment that opens its line is extracted so ai-slop-ignore works; stale never-reached comments name Css.
Natlang column, Tier B exclusion list, STYLE_LANGS in the adding-a-rule step, suppression ceilings, tree-sitter-css beside the other grammars, a pt-BR clean twin, and a fatal error when a Design2Code or WebSight row has no text.
@mgiovani
mgiovani force-pushed the feat/design-style-layer branch from a77cfe3 to 93c20a6 Compare October 14, 2026 15:58
@mgiovani mgiovani changed the title feat: style layer and design rules for gradient text, purple gradients and violet hexes feat: add design group (gradient text, purple-blue gradient, violet accent, violet hex) Oct 14, 2026
@mgiovani mgiovani changed the title feat: add design group (gradient text, purple-blue gradient, violet accent, violet hex) feat: add design group (gradient text, purple-blue gradient, violet accent, violet hex, shadcn violet theme) Oct 14, 2026
…, tighten SLOP056 markers

SLOP053 now needs an opaque stop on the blue side and one in the purple core, pairs
from-/via-/to- stops only inside one variant and never within one hue name. SLOP056 stops
counting --card and --radius as shadcn markers. Unitless hsl() saturation and lightness read
as percentages, the violet floor moves to 248 so hex and oklch agree on indigo, SLOP054 reads
8-digit hex, SLOP052 reads case-insensitive and listed background-clip values. Messages name
the matched utility or property. Unterminated quotes keep their last character, a rejected
set of <style> ranges parses one range at a time, and // lines inside a /* */ block are not
SCSS comments.
…yle gate

lint_file returns early when no enabled rule (built-in or custom) lists the lang, so a run
that disables the design group no longer parses stylesheets. wants_style checks enabled and
lang before the group lookup. .min.css bundles resolve to no Lang.
…s bench

git_clone_ref, git_clone_before and git_clone_sha land through one helper (prompt guard on
every step), the four app-builder entries share their caveats and cells, the pinned-set
fetch reuses read_text_files. Adds tests for git_files, pinned_set_revision and
fetch_git_pinned_set, and derives the APPLICABLE design check from docs/rules.md.
@mgiovani
mgiovani merged commit 41d95f9 into main Oct 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

design: style layer (Lang::Css, StyleDoc, color helpers, design group)

1 participant