Neue Skills, Referenzen & OpenWiki-Doku integriert
Umfangreiche Erweiterung der Skill-Bibliothek: Neue Skills für Humanisierung (Englisch/PT-BR), Design-Validierung, AI-SEO und Coolify-Deployment inkl. Regelwerke, Presets, Pattern-Referenzen, Testfälle und Automatisierungsskripte. Zusätzliche Skills für Revenue-Centric Design, Pier Cloud, OKF, Lebenslauf- und LinkedIn-Optimierung sowie zahlreiche Referenzdateien, Checklisten und YAML/JSON/Markdown-Templates. Einführung einer vollständigen OpenWiki-Dokumentation mit Architektur-, Domain- und Workflow-Beschreibungen, zentralem Index und automatisierten Updates. Modularer Aufbau, restriktive Lizenzen und umfassende Qualitäts- und Evaluationsmechanismen für alle neuen Inhalte.
This commit is contained in:
263
.github/skills/slop-eval/SKILL.md
vendored
Normal file
263
.github/skills/slop-eval/SKILL.md
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
---
|
||||
name: slop-eval
|
||||
description: >
|
||||
Objectively evaluate a UI/web design against the pols.dev anti-slop design
|
||||
law: detect catalogued slop tells with cited evidence, score 8 weighted
|
||||
axes (color, type, components, layout, motion, execution, signature,
|
||||
cohesion), and emit a Slop Report with a 0–100 Slop Index and grade. Use
|
||||
when the user asks to "evaluate design slop", "slop report", "is this
|
||||
design AI slop", "audit this landing page design", "de-slop review", or
|
||||
wants an objective score of how generic/machine-made a design looks. To
|
||||
fix text (not design), use human-ai or humanizar skills instead.
|
||||
metadata:
|
||||
author: https://ft.ia.br
|
||||
version: "1.0.0"
|
||||
date: 2026-07-16
|
||||
repository: https://github.com/fabricioctelles/skills
|
||||
license: Apache-2.0
|
||||
category: code-quality-and-review
|
||||
---
|
||||
|
||||
# Slop Eval
|
||||
|
||||
Evaluate a design the way `skill-evaluation` evaluates a skill: every finding
|
||||
cites concrete evidence, every axis gets a 0–100 score, arithmetic runs
|
||||
through a script, and the output is a structured report — never a vibe check.
|
||||
|
||||
The tell catalog lives in `references/tells.md`; read it before sweeping.
|
||||
The positive rubric (signature formula, cohesion checks, slop→premium pairs)
|
||||
lives in `references/premium-markers.md`; read it before scoring Axes 7–8.
|
||||
|
||||
## Source
|
||||
|
||||
- [The pols.dev anti-slop design law](https://pols.dev/slop.md) — the tell
|
||||
catalog, absolute rules, and signature formula are distilled from it.
|
||||
- Method modeled on
|
||||
[skill-evaluation](https://github.com/fabricioctelles/skills/tree/main/skills/skill-evaluation)
|
||||
(cite-or-cut, weighted axes, scripted scoring, failure-mode diagnosis).
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
| `target` | What to evaluate: live URL, screenshot(s), code path, or Figma export | Ask user |
|
||||
| `brief` | Brand brief or explicit user directions the design followed | None |
|
||||
| `output` | Path to write the report | `./SLOP-REPORT.md` |
|
||||
|
||||
Write the report in the language the user is speaking; keep tell IDs and
|
||||
names in English so they stay greppable against the catalog.
|
||||
|
||||
## Evidence channels
|
||||
|
||||
What you can verify depends on what you were given. Never score a check you
|
||||
could not observe — mark it **Unverifiable** and exclude it (like N/A in
|
||||
skill-evaluation).
|
||||
|
||||
| Channel | Can verify | Cannot verify |
|
||||
|---------|-----------|---------------|
|
||||
| Code (CSS/JSX/HTML) | Fonts, hex values, gradients, shadows, radii, `opacity:0` gating, icon imports, layout skeletons | Optical centering, rendered contrast, seams, whether controls respond |
|
||||
| Screenshot(s) | Everything visual: palette, type, layout, alignment, centering, clipping, contrast, seams | Hover/scroll motion, dead controls, invisible-content trap, responsive behavior |
|
||||
| Live URL (browse + screenshot) | All of the above plus interactions, motion, fold ownership | Only what you didn't exercise |
|
||||
|
||||
With code, grep before you stare: `fonts.googleapis|next/font`,
|
||||
`lucide-react`, `linear-gradient`, `box-shadow`, `border-radius: *9999`,
|
||||
`backdrop-filter`, `opacity: *0`, `initial={{ *opacity: *0`,
|
||||
`overflow: *hidden`, `clip-path`, `position: *fixed`. Each hit is a lead,
|
||||
not a verdict — confirm against the catalog entry before recording it.
|
||||
|
||||
## Evidence acquisition SOP
|
||||
|
||||
Route by what the `target` is; always end with an evidence inventory
|
||||
(what was captured, what is Unverifiable) — it feeds the report header.
|
||||
|
||||
**Live URL** — the richest channel; prefer it whenever reachable.
|
||||
Use whatever browser automation this session has (a browser MCP such as
|
||||
Playwright or Chrome DevTools, or `npx playwright screenshot` as the
|
||||
no-MCP fallback) and capture, saving every artifact to the scratchpad so
|
||||
findings can cite `file + region`:
|
||||
|
||||
1. Load at desktop (1440×900) and mobile (390×844); wait for network idle.
|
||||
2. Full-page screenshot of both viewports **immediately after load,
|
||||
before any scrolling** — sections sitting at `opacity:0` waiting for a
|
||||
scroll reveal show up blank here (M1 evidence).
|
||||
3. Scroll pass top to bottom, then a second full-page capture; diff the
|
||||
two mentally for reveal-gated content, seams (C11, X13), and fold
|
||||
ownership (L16).
|
||||
4. Interaction pass: hover the primary CTA, one card, one nav link
|
||||
(M2–M4); click every tab, accordion, toggle, and button (M8); Tab
|
||||
through the page and confirm a visible focus ring (X14).
|
||||
5. Zoom crops at 2x of: anything near a clipped edge (X2), circled/tiled
|
||||
numbers and icons (X1), pricing columns side by side (X3), button
|
||||
labels (X5).
|
||||
6. Pull the rendered sources for the code-channel greps: font names from
|
||||
the network panel or `<link>`/`@font-face`, computed hex values from
|
||||
the stylesheets.
|
||||
|
||||
No browser automation available → fetch the HTML/CSS (`curl`) and run the
|
||||
code channel on it, ask the user for full-page desktop + mobile prints,
|
||||
and mark every visual-only and interaction check Unverifiable until the
|
||||
prints arrive. Never score a visual check from raw HTML.
|
||||
|
||||
**Screenshots** — Read each image. If only partial crops were provided,
|
||||
ask for full-page desktop + mobile before sweeping (a hero-only print
|
||||
cannot support L11, L15, or the cohesion axis). All interaction checks
|
||||
(M1, M8, X14, hover tells) are Unverifiable.
|
||||
|
||||
**Code path** — run the greps, read every file they hit, plus the layout/
|
||||
page components and global styles. If the project runs locally, start its
|
||||
dev server and continue under the Live URL SOP — code plus a live render
|
||||
is the only combination that can verify everything.
|
||||
|
||||
**Figma export** — treat as Screenshots for visual tells; additionally
|
||||
fonts, hex values, and spacing are exact from the file. Motion and
|
||||
interaction axes are Unverifiable (score `NA` for Axis 5 unless
|
||||
prototypes were shared).
|
||||
|
||||
## Axes and weights
|
||||
|
||||
| # | Axis | Weight | Scored from |
|
||||
|---|------|--------|-------------|
|
||||
| 1 | Color & Light | 2x | Tells C1–C15 |
|
||||
| 2 | Typography & Copy | 2x | Tells T1–T10, W1–W3 |
|
||||
| 3 | Components & Ornament | 1x | Tells K1–K27 |
|
||||
| 4 | Layout & Composition | 2x | Tells L1–L21 |
|
||||
| 5 | Motion & Interaction | 1x | Tells M1–M8 |
|
||||
| 6 | Execution & Craft | 2x | Tells X1–X14 |
|
||||
| 7 | Signature & Uniqueness | 3x | 7-element formula (positive rubric) |
|
||||
| 8 | Cohesion | 2x | 4 checks (positive rubric) |
|
||||
|
||||
Axis 7 carries the heaviest weight on purpose: the law's deepest rule is
|
||||
that dodging the tell list is still slop — a page with zero tells and no
|
||||
signature is unfinished work wearing restraint as an alibi.
|
||||
|
||||
## Scoring
|
||||
|
||||
**Axes 1–6 (tell-counted).** Count confirmed tells on the axis by severity,
|
||||
then: `score = max(0, 100 − 30·critical − 15·major − 5·minor)`. Run
|
||||
`scripts/score.py axis CRIT MAJOR MINOR` — don't do it by hand. One tell,
|
||||
one count: a pattern repeated across sections is still one tell (note the
|
||||
repetition in the evidence; repetition may upgrade minor → major where the
|
||||
catalog says so).
|
||||
|
||||
**Axis 7 (Signature).** Score each of the 7 formula elements 0 (absent),
|
||||
50 (attempted, weak), or 100 (strong) per the rubric in
|
||||
`premium-markers.md`; the axis is their mean.
|
||||
|
||||
**Axis 8 (Cohesion).** Same 0/50/100 on the 4 cohesion checks; mean.
|
||||
|
||||
**Compounding rule.** Three or more *major* layout tells on one page cap
|
||||
Axis 4 at 40 — a page assembled from known skeletons is slop no matter how
|
||||
clean each block is.
|
||||
|
||||
**Gates** (pass as `--cap` to the overall run):
|
||||
- **Signature gate:** Axis 7 < 40 caps the overall at 59 (grade C max). No
|
||||
amount of clean spacing rescues a page with no signature.
|
||||
- **Absolute-rule gate:** any confirmed critical tell caps the overall at
|
||||
69 (no grade A with broken execution).
|
||||
|
||||
**Overall & Slop Index.**
|
||||
|
||||
```
|
||||
overall = sum(axis_score × weight) / sum(weight) # capped by gates
|
||||
Slop Index = 100 − overall
|
||||
```
|
||||
|
||||
Run `scripts/score.py overall 1:80:2 2:65:2 ... [--cap 59] [--cap 69]`.
|
||||
Unverifiable axes score `NA` and drop out of both sums. `--fail-below N`
|
||||
exits non-zero for CI gating, e.g. gating a PR on its preview deploy:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/slop-gate.yml (step excerpt)
|
||||
- name: Slop gate
|
||||
run: |
|
||||
# run slop-eval against $PREVIEW_URL, export each axis score, then:
|
||||
python3 skills/slop-eval/scripts/score.py overall \
|
||||
1:$A1:2 2:$A2:2 3:$A3:1 4:$A4:2 5:$A5:1 6:$A6:2 7:$A7:3 8:$A8:2 \
|
||||
--fail-below 40
|
||||
```
|
||||
|
||||
## Grade scale
|
||||
|
||||
| Grade | Overall | Slop Index | Verdict |
|
||||
|-------|---------|------------|---------|
|
||||
| A | 80–100 | 0–20 | Premium — deliberate, signed, executed |
|
||||
| B | 60–79 | 21–40 | Considered — mostly deliberate, some defaults |
|
||||
| C | 40–59 | 41–60 | Generic — clean but templated or unsigned |
|
||||
| D | 20–39 | 61–80 | Slop — assembled from presets |
|
||||
| F | 0–19 | 81–100 | Pure slop |
|
||||
|
||||
## Absolute rules check
|
||||
|
||||
Six execution laws, each pass/fail/unverifiable, reported in their own
|
||||
table. Any **fail** is a critical tell (counts on its axis AND triggers the
|
||||
absolute-rule gate):
|
||||
|
||||
1. **Content visible by default** — nothing gated on an entrance animation
|
||||
(`opacity:0` + reveal) (M1)
|
||||
2. **Clear the cut** — no text/control sliced by clip, notch, overflow, or
|
||||
fixed height (X2, X11)
|
||||
3. **Parallel alignment** — comparable columns share baselines; buttons
|
||||
anchored (X3)
|
||||
4. **Real centering** — everything meant to be centered is, mathematically
|
||||
and optically (X1)
|
||||
5. **Legible contrast** — every text clears its background by a real value
|
||||
gap (X5)
|
||||
6. **Controls work** — every interactive-looking control responds (M8)
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Gather evidence** — route the `target` through the Evidence
|
||||
acquisition SOP above. Done when the evidence inventory states what
|
||||
was captured and what is Unverifiable.
|
||||
2. **Read `references/tells.md`** — the catalog you sweep against.
|
||||
3. **Sweep axes 1–6** — walk the catalog group by group. **Cite-or-cut**:
|
||||
a tell is only recorded with concrete evidence (hex value, font name,
|
||||
`file:line`, or screenshot region); no evidence, no tell. Check each
|
||||
candidate against its premium-pair note — the crafted version of a
|
||||
pattern is not the tell. Done when every catalog group has been swept
|
||||
and every recorded tell carries a citation.
|
||||
4. **Run the absolute rules check** — all six, pass/fail/unverifiable with
|
||||
evidence.
|
||||
5. **Score Axes 7–8** — read `references/premium-markers.md`, score the 7
|
||||
signature elements and 4 cohesion checks with one-line justifications
|
||||
each. Done when all 11 items carry a score and a justification.
|
||||
6. **Compute** — `score.py axis` per tell-counted axis, then
|
||||
`score.py overall` with weights and any triggered `--cap`. Never
|
||||
hand-compute.
|
||||
7. **Write the report** — read `references/output-template.md` and emit
|
||||
exactly that structure to `output`, ending with the 3–5 prioritized
|
||||
fixes that would move the score most (biggest weighted deltas first;
|
||||
a missing signature usually outranks any single tell).
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **The brief overrides the law.** If the user or brand explicitly directed
|
||||
a choice (a color, a layout, an effect), it is not a tell — the law
|
||||
itself says the user's word wins 100%. Ask for the brief when the design
|
||||
clearly follows one; note excluded tells in the report.
|
||||
- **Context flips a tell.** Mono on real data is correct; a populated,
|
||||
real-feeling product window is a signature, not the fake-window tell; a
|
||||
tight micro-grid with texture is premium, a full-page graph paper is
|
||||
slop. Always check the premium pair before recording.
|
||||
- **Don't reward the clean miss.** Zero tells with a weak signature is the
|
||||
most common failure of designs that *tried* to avoid slop. The signature
|
||||
gate exists for this — apply it without mercy.
|
||||
- **Severity discipline.** Critical is reserved for *broken* (the six
|
||||
absolute rules). A blue-purple gradient is loud but not broken: major.
|
||||
- **One-axis bleed.** Some tells could sit on two axes (cut-off glow is
|
||||
color and execution). The catalog assigns each tell to exactly one axis —
|
||||
count it only there.
|
||||
- **Portfolio tells.** L19 (recycling your own house style) needs prior
|
||||
work from the same author to verify; without it, mark Unverifiable
|
||||
rather than guessing.
|
||||
|
||||
## Quality checklist
|
||||
|
||||
Final gate before delivering — each item re-checks a workflow step:
|
||||
|
||||
- [ ] every recorded tell has ID + severity + citation (step 3)
|
||||
- [ ] every unverifiable check is marked, not silently passed (steps 1, 4)
|
||||
- [ ] all 6 absolute rules reported (step 4)
|
||||
- [ ] all 11 signature/cohesion items scored with justification (step 5)
|
||||
- [ ] caps applied when gates triggered; math from `score.py` only (step 6)
|
||||
- [ ] report matches the template, fixes ranked by weighted impact (step 7)
|
||||
130
.github/skills/slop-eval/references/output-template.md
vendored
Normal file
130
.github/skills/slop-eval/references/output-template.md
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
# Output Template
|
||||
|
||||
Emit the Slop Report exactly in this structure (step 7 of the workflow),
|
||||
in the user's language, keeping tell IDs/names in English.
|
||||
|
||||
```markdown
|
||||
# Slop Report — {design/site name}
|
||||
|
||||
> Evaluated: {date}
|
||||
> Target: {URL / path / screenshot description}
|
||||
> Evidence channels: {code / screenshots / live URL} — unverifiable: {list or "none"}
|
||||
> Brief provided: {yes — summarize / no}
|
||||
> Evaluator: slop-eval v1.0.0
|
||||
> Framework: [pols.dev anti-slop design law](https://pols.dev/slop.md)
|
||||
|
||||
## Summary
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| **Slop Index** | **{100 − overall}/100** |
|
||||
| Overall score | {overall}/100 {(capped by: signature gate / absolute-rule gate)} |
|
||||
| Grade | {A–F} — {verdict from the grade scale} |
|
||||
| Tells detected | {n} ({c} critical, {m} major, {k} minor) |
|
||||
| Signature (Axis 7) | {score}/100 |
|
||||
| Absolute rules | {p} pass · {f} fail · {u} unverifiable |
|
||||
|
||||
{One-paragraph verdict in plain words: what this design is, what sinks or
|
||||
carries it. Lead with the single most consequential finding.}
|
||||
|
||||
## Scorecard
|
||||
|
||||
| # | Axis | Weight | Score | Tells (crit/maj/min) |
|
||||
|---|------|--------|-------|----------------------|
|
||||
| 1 | Color & Light | 2x | {n}/100 | {c}/{m}/{k} |
|
||||
| 2 | Typography & Copy | 2x | {n}/100 | {c}/{m}/{k} |
|
||||
| 3 | Components & Ornament | 1x | {n}/100 | {c}/{m}/{k} |
|
||||
| 4 | Layout & Composition | 2x | {n}/100 {(compounding cap)} | {c}/{m}/{k} |
|
||||
| 5 | Motion & Interaction | 1x | {n or NA}/100 | {c}/{m}/{k} |
|
||||
| 6 | Execution & Craft | 2x | {n}/100 | {c}/{m}/{k} |
|
||||
| 7 | Signature & Uniqueness | 3x | {n}/100 | — |
|
||||
| 8 | Cohesion | 2x | {n}/100 | — |
|
||||
|
||||
## Detected tells
|
||||
|
||||
Ordered critical → major → minor. Every row cites evidence; a tell with no
|
||||
citation does not appear.
|
||||
|
||||
| ID | Tell | Sev | Evidence |
|
||||
|----|------|-----|----------|
|
||||
| {M1} | {Invisible-content trap} | crit | {file:line, hex, font name, or screenshot region — concrete} |
|
||||
| ... | | | |
|
||||
|
||||
{Tells excluded because the brief directed them, if any:}
|
||||
| ID | Tell | Excluded because |
|
||||
|----|------|------------------|
|
||||
|
||||
## Absolute rules
|
||||
|
||||
| # | Rule | Status | Evidence |
|
||||
|---|------|--------|----------|
|
||||
| 1 | Content visible by default | {pass/FAIL/unverifiable} | {evidence} |
|
||||
| 2 | Clear the cut | {…} | {…} |
|
||||
| 3 | Parallel alignment | {…} | {…} |
|
||||
| 4 | Real centering | {…} | {…} |
|
||||
| 5 | Legible contrast | {…} | {…} |
|
||||
| 6 | Controls work | {…} | {…} |
|
||||
|
||||
## Signature assessment (Axis 7)
|
||||
|
||||
| # | Element | Score | Justification |
|
||||
|---|---------|-------|---------------|
|
||||
| S1 | Signature artifact | {0/50/100} | {one line} |
|
||||
| S2 | Atmosphere | {…} | {…} |
|
||||
| S3 | Layered depth | {…} | {…} |
|
||||
| S4 | Character display face | {…} | {…} |
|
||||
| S5 | Bespoke silhouette | {…} | {…} |
|
||||
| S6 | Treated nav | {…} | {…} |
|
||||
| S7 | Real specificity | {…} | {…} |
|
||||
|
||||
## Cohesion assessment (Axis 8)
|
||||
|
||||
| # | Check | Score | Justification |
|
||||
|---|-------|-------|---------------|
|
||||
| H1 | One palette | {0/50/100} | {one line} |
|
||||
| H2 | One type voice | {…} | {…} |
|
||||
| H3 | One system | {…} | {…} |
|
||||
| H4 | Composed from the brief | {…} | {…} |
|
||||
|
||||
## Prioritized fixes
|
||||
|
||||
3–5, ranked by weighted score impact — a missing signature (3x axis)
|
||||
usually outranks any single tell. Each fix names the tell/element, cites
|
||||
its evidence, and prescribes the premium version from the slop→premium
|
||||
pairs table where one exists.
|
||||
|
||||
### 1. {fix}
|
||||
|
||||
**Evidence:** {citation} · **Impact:** {axis, est. delta}
|
||||
|
||||
**Do:** {specific prescription — the premium pair, not just "remove it"}
|
||||
|
||||
### 2. {fix}
|
||||
|
||||
...
|
||||
|
||||
## Grade scale
|
||||
|
||||
{copy the Grade scale table from SKILL.md}
|
||||
|
||||
---
|
||||
|
||||
*Generated by [slop-eval](https://github.com/fabricioctelles/skills) v1.0.0
|
||||
against [the pols.dev anti-slop design law](https://pols.dev/slop.md).*
|
||||
```
|
||||
|
||||
## Comparison mode
|
||||
|
||||
When evaluating two designs (or before/after), add a side-by-side axis
|
||||
table. NA rows drop from each side's weighted math.
|
||||
|
||||
```markdown
|
||||
## Comparison: {A} vs {B}
|
||||
|
||||
| # | Axis | {A} | {B} | Delta |
|
||||
|---|------|-----|-----|-------|
|
||||
| 1 | Color & Light | 40 | 85 | +45 |
|
||||
| ... | | | | |
|
||||
| **Overall** | | **38** | **74** | **+36** |
|
||||
| **Slop Index** | | **62** | **26** | **−36** |
|
||||
```
|
||||
89
.github/skills/slop-eval/references/premium-markers.md
vendored
Normal file
89
.github/skills/slop-eval/references/premium-markers.md
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
# Premium markers — the positive rubric
|
||||
|
||||
Axes 7 and 8 measure what the design *did*, not what it avoided. A page can
|
||||
dodge every tell and still be slop because nothing was invented; these two
|
||||
axes are where that failure is priced in.
|
||||
|
||||
## Axis 7 — Signature & Uniqueness
|
||||
|
||||
The uniqueness formula from the law:
|
||||
|
||||
```
|
||||
uniqueness = one signature artifact + atmosphere + layered depth
|
||||
+ character display face + one bespoke silhouette
|
||||
+ a treated nav + real specificity
|
||||
```
|
||||
|
||||
Score each element 0 / 50 / 100 with a one-line justification; the axis is
|
||||
the mean of the seven.
|
||||
|
||||
| # | Element | 0 (absent) | 50 (attempted) | 100 (strong) |
|
||||
|---|---------|------------|----------------|---------------|
|
||||
| S1 | Signature artifact | No custom focal object; hero is text on a fill or a stock/prop visual | A custom visual exists but is generic enough to paste onto another site | ONE high-effort focal object (crafted SVG scene, populated product artifact, illustration, render) that could only belong to this brand |
|
||||
| S2 | Atmosphere | Flat color fill behind everything | Some texture/tone in the hero only (dies at the fold — see L11) | The background is a composed environment (scene, texture, grain, light) carried down the whole scroll |
|
||||
| S3 | Layered depth | One flat plane | Two reads, nothing crossing a boundary | Foreground copy / midground focal object / background scene, with at least one element overlapping or bleeding across a layer edge |
|
||||
| S4 | Character display face | Identity rests on a neutral grotesque or a Google-shelf default (T1) | A distinctive face chosen by reputation, not brief (T9) | A licensed/self-hosted display face with real personality, set large, chosen for this brief (body may stay neutral; system-ui is a legitimately neutral body) |
|
||||
| S5 | Bespoke silhouette | Every shape is a default rectangle/pill | One mild customization (a radius decision, a simple notch) | One unmistakable custom-cut geometry signing the page (a receipt-torn edge, an invented marker, a specific arrow drawn for the system) |
|
||||
| S6 | Treated nav | Default flush row of links bolted on top | Centered or contained but generic | The nav is a decision: floated pill, real presence, brand marks threaded in — it belongs to the system |
|
||||
| S7 | Real specificity | Placeholder logos, fake names, lorem-adjacent copy | Mixed: some real data among placeholders | Real recognizable logos honestly claimable, real names/data inside the product shot, copy written for this product |
|
||||
|
||||
Signature gate: axis mean < 40 caps the overall at 59. Even the most
|
||||
minimal premium site has at least the signature artifact and a character
|
||||
face.
|
||||
|
||||
## Axis 8 — Cohesion
|
||||
|
||||
"Cohesion is the whole game": the loudest observed failure is not tells,
|
||||
it is individually-fine parts that don't belong to each other. Score each
|
||||
check 0 / 50 / 100; the axis is the mean of the four.
|
||||
|
||||
| # | Check | What 100 looks like |
|
||||
|---|-------|---------------------|
|
||||
| H1 | One palette, held with discipline | A monochrome or tightly-related set; adjacent sections share or hand off tone. 0 = "blue AND green AND a warm accent", each fine alone, ugly together. |
|
||||
| H2 | One type voice | A single family across weights/optical sizes, or one display + one quiet neutral — never two display faces arguing. |
|
||||
| H3 | One system | Nav, buttons, arrows, radii, borders, background speak one language (sharp everywhere, one arrow reused, one gradient threaded through). A page of mismatched fine components reads cheap. |
|
||||
| H4 | Composed from the brief | Sections designed from what this product actually is — not known skeletons restacked and recolored, not a reference site's content reproduced. Reference = direction, never a stencil. |
|
||||
|
||||
## Slop→premium pairs (check before recording a tell)
|
||||
|
||||
The same element is slop as the obvious preset and premium when clearly
|
||||
made on purpose for this one screen:
|
||||
|
||||
| Pattern | Slop version | Premium version |
|
||||
|---------|-------------|-----------------|
|
||||
| Glass | Frosted box + blue glow ignoring its background; banding, leak, halo, pop (K25) | Material over a backdrop worth refracting: refraction, edge dispersion, top-lip highlight, light frost, tuned inner+drop shadows. The gloss is the good part — keep it, fix everything around it |
|
||||
| Borders | Hard contrasting 1px line on every box (K13) | Self-colored border: surface value shifted a hair, 1px stroke at the surface's own color low-opacity, soft top inner highlight — an edge you feel |
|
||||
| Accent bar | Straight preset bar on a card edge (K15) | Invented silhouette: diagonal cut-in, chamfer, notch, custom bracket — geometry drawn on purpose |
|
||||
| Icons | Pack icons in tiles (K1/K4/K24); zero icons as over-correction | Bare bespoke marks in one house style, consistent stroke/corner/grid |
|
||||
| Shadow | Symmetric black bloom on everything (X6) | Tight, low-offset, small blur, tinted to surface or element color, cast from one direction — or no shadow, depth from tone |
|
||||
| Glow/light | Blue-purple bloom, centered halo (C1/C5) | Specific, unexpected color with chosen direction and falloff: a warm volumetric rake, a single beam |
|
||||
| Grid | Full-page faint graph paper (C15) | Tight textured micro-grid behind one panel; sparse blueprint marks (ruler ticks, corner crops, dashed guides) |
|
||||
| Gradient | Smooth banded wash (C14) | Grain/noise dithered into every large transition — a surface that feels physical |
|
||||
| App window | Empty generic mock with traffic lights (K7) | Detailed, fully-populated, real-feeling product UI, floated with depth, clipped at an edge — and only when a product UI actually exists |
|
||||
| Footer wordmark | Text pasted big: off-center, clipped, no treatment (L20) | Anchored flush to the bottom, above the texture, deliberate case + spacing, intentional bleed |
|
||||
| Motion | Entrance reveals gating content (M1); boops and underline fills (M2/M3) | Scroll-authored motion on already-visible elements, authored micro-interactions tuned for one element, gated behind prefers-reduced-motion |
|
||||
| Inset island | The default closing CTA panel every time (L8) | A section floated with margin on all sides, on its own surface + grain, used once where detachment means something |
|
||||
| Noise | Grain sheet over text and controls (K26 — grain over content) | Grain on the substrate at very low opacity: felt, not seen. One masked display word can carry grain as a chosen move |
|
||||
|
||||
## Field notes that outrank everything
|
||||
|
||||
- **Decide the signature FIRST**, then build sections around it. Miss the
|
||||
artifact and no amount of clean spacing rescues the page.
|
||||
- **Clean is the floor, never the achievement.** Correct spacing + quiet
|
||||
type + zero authored moments = unfinished work wearing restraint as an
|
||||
alibi. Calm is a style; empty is a miss.
|
||||
- **"Creative" is not "realistic".** Photoreal stock reads as the opposite
|
||||
of creative; an authored treatment in ONE medium (cyanotype, riso, pixel
|
||||
art, one illustration style) auto-coheres and signs the page.
|
||||
- **Type without the Google shelf:** Fontshare (Pally, Gambarino, Sentient,
|
||||
Tanker), Velvetyne, or licensed faces (Pangram Pangram, Displaay, Klim),
|
||||
self-hosted. Clash Display / General Sans already read generic. View
|
||||
candidates rendered before picking; never name faces from memory.
|
||||
- **Component libraries are legitimate foundations** (Motion, shadcn/ui,
|
||||
tailark, motion-primitives, kokonut) — take the accessible behavior,
|
||||
throw away the generic styling, art-direct on top. De-slop every prebuilt
|
||||
block as if it were your own work.
|
||||
- **The antidote to the slop floor is a specific visual reference.** When
|
||||
prescribing fixes, point to starting from a concrete reference (Mobbin,
|
||||
Godly, Awwwards-tier sites) — it forces the work off the statistical
|
||||
center. Language from the reference, never its content (H4).
|
||||
145
.github/skills/slop-eval/references/tells.md
vendored
Normal file
145
.github/skills/slop-eval/references/tells.md
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
# The tell catalog
|
||||
|
||||
Distilled from [the pols.dev anti-slop design law](https://pols.dev/slop.md).
|
||||
Each tell has an ID (grep the report with it), a severity, and what counts
|
||||
as evidence. Severity: **crit** = broken (absolute-rule violation),
|
||||
**major** = a recognized slop signature, **minor** = a default reached for
|
||||
without intent.
|
||||
|
||||
A tell is recorded once per page, with its worst instance cited. Where a
|
||||
"premium pair" note exists, the crafted version is NOT the tell — check it
|
||||
before recording.
|
||||
|
||||
## C — Color & Light (Axis 1)
|
||||
|
||||
| ID | Tell | Sev | Evidence to look for |
|
||||
|----|------|-----|----------------------|
|
||||
| C1 | Blue→purple gradient | major | The single most recognizable slop move: soft blue-to-purple anywhere (backgrounds, buttons, borders, avatars). Any glowy two-adjacent-hue gradient counts. (Community name: the "AI purple problem".) |
|
||||
| C2 | Purple default palette | minor | Purple as the unexamined brand hue; upgrade to C1 when gradiated with blue. |
|
||||
| C3 | Pastel candy gradient background | major | Butter-yellow→peach→strawberry-milk (`#ffe6a8`→`#ffc0da` family), mint-to-lavender, sherbet washes filling a page/section. |
|
||||
| C4 | Drifting aurora blobs | major | 2–4 big blurred radial blobs at ~0.5 opacity, often `mix-blend-mode: multiply` + `blur()`, melting into a pastel aurora behind content. Muted hexes don't rescue it. |
|
||||
| C5 | Radial glow halo behind an object | major | Concentric bloom centered behind a hero object. Warm color doesn't rescue a symmetric halo — light comes from a direction or not at all. |
|
||||
| C6 | Cool blue-charcoal dark default | major | The slate-indigo "serious dark product" base (~`#0c0e15`), bluer panels, lilac/periwinkle accent. Dark that nobody chose — the night-mode twin of C1, equally recognizable, equally default. |
|
||||
| C7 | Cream/beige "editorial" default | minor | Warm cream/bone as the reflexive "tasteful premium" background — the new blue-purple. Major when it carries the whole brand across every surface. |
|
||||
| C8 | Slop gray (UI-kit neutral) | minor | gray-100/200 family (`#f3f4f6`, `#eceef2`, `#e7ecf3`) as footer band, card fill, or page base — a wireframe left at its default. |
|
||||
| C9 | Saturated accent sprayed everywhere | major | One vivid mid-saturation hue on the accent word, eyebrow dot, button fill, and labels at once. Premium accents are tonal (value-shifted, desaturated), not poster-bright. |
|
||||
| C10 | Colliding colors / muddy wash | major | Two saturated unrelated hues fighting; an accent belonging to no system; a dim brown/grey-beige envelope under a fine component. |
|
||||
| C11 | Hard color seams between sections | major | A gradient/glow that dies at a section boundary; the page should resolve one section's color into the next. (Deliberate breaks — a footer stepping onto its own floor — are fine.) |
|
||||
| C12 | Background glow blob | minor | Soft radial accent bleeding from a corner/edge of a dark section for "atmosphere". See X12 for the clipped version. |
|
||||
| C13 | Gradient-filled headline text | major | `background-clip: text` pouring magenta-purple-cyan (or blue-cyan) into display type. |
|
||||
| C14 | Banded gradient | minor | A large color transition with visible stripes and no grain/dither. Premium pair: grainy gradients (noise dithered in) read as expensive. |
|
||||
| C15 | Full-page grid / graph-paper background | major | Faint module grid (often radial-masked) laid under the whole page, even at low opacity. Premium pair: a tight, textured micro-grid behind one panel, or sparse blueprint marks (ruler ticks, crop marks). |
|
||||
|
||||
## T — Typography · W — Copy (Axis 2)
|
||||
|
||||
| ID | Tell | Sev | Evidence to look for |
|
||||
|----|------|-----|----------------------|
|
||||
| T1 | Google-shelf signature face | major | The identity carried by a free Google default. Rejected rotation — sans: Inter, Space Grotesk, Sora, Syne, Archivo, Onest, Darker Grotesque, Geologica, Hanken Grotesk, Spline Sans, Schibsted Grotesk, Gabarito, Figtree, Quicksand; serif: Fraunces, Cormorant, Bodoni/Didones, Playfair, Petrona, Hedvig Letters Serif, Brygada 1918, Young Serif; mono: JetBrains Mono, IBM Plex Mono, Spline Sans Mono, Fragment Mono. Inter as body is fine; Inter as the signature is the tell. |
|
||||
| T2 | Recognizable slop pairing | major | Fraunces+Work Sans, Space Grotesk+Inter, Sora+JetBrains Mono, or any serif-display+clean-sans house pairing reused across brands. |
|
||||
| T3 | Didone-as-luxury reflex | major | Bodoni/Didot/Playfair reached for because something "needs to feel expensive", usually letterspaced full caps. A Didone chosen on autopilot is slop. |
|
||||
| T4 | Mono as the house voice | minor | Monospace on copyright lines, eyebrows, captions, labels everywhere to signal "technical". Mono is correct only for genuine data (timestamps, codes, prices, tables). |
|
||||
| T5 | One label treatment everywhere | minor | The identical tracked-out-caps (or mono) costume on eyebrow, buttons, figure numbers, nav, and colophon at once. Different roles need different treatments. |
|
||||
| T6 | Letterspaced serif wordmark | minor | Brand name in all-caps serif with wide tracking and nothing else — instant "luxury" logo. (SaaS twin: K12.) |
|
||||
| T7 | Multi-line headline + dangling accent | major | Display line wrapping to 3–4 stacked rows; worse when the one colored/italic accent word lands stranded on the last line. Hold display to 1–2 composed lines, one coherent emphasis. |
|
||||
| T8 | Cramped display type | major | Big numbers/words with negative tracking until glyphs nearly touch, separators buried ("0·fail"). Large type needs air. |
|
||||
| T9 | The "tasteful" font swap | minor | Reaching for the known good alternative (Clash Display, General Sans, Big Shoulders, Newsreader, Instrument Serif, Bricolage) *because it's the reputed safe pick*. Picking type by reputation instead of by the brief is the tell. |
|
||||
| T10 | Novelty rounded display + system body | major | Fat bubbly display faces (Baloo, Fredoka, Chewy, Lobster, Bagel Fat One) carrying headings, wordmark, and prices over a default system-ui body. |
|
||||
| W1 | Em dashes as AI voice | minor | Em-dash-heavy copy — the classic AI writing tell. Hyphen, colon, or split the sentence. |
|
||||
| W2 | Wall of copy | minor | Many stacked lines of filler text where hierarchy and visuals should carry meaning. Premium is terse. |
|
||||
| W3 | Fake-but-impressive metrics | major | Invented social proof in copy: "velocity jumped 32%", fabricated customer names/titles ("VP Engineering, Northwind Labs"). |
|
||||
|
||||
## K — Components & Ornament (Axis 3)
|
||||
|
||||
| ID | Tell | Sev | Evidence to look for |
|
||||
|----|------|-----|----------------------|
|
||||
| K1 | Icon-pack icons everywhere | minor | Uniform thin-stroke line icons (lucide-react and kin) on every feature/section. Also covers "custom" redrawn versions of the same generic glyphs, and emojis standing in for icons. Premium pair: a bespoke set with an invented construction. |
|
||||
| K2 | Pill / eyebrow badge | minor | The capsule above the hero headline (tiny icon + short text). Default hero decoration. |
|
||||
| K3 | Glowy pill buttons | major | Fully-rounded gradient-filled buttons with a soft blurred glow beneath. |
|
||||
| K4 | Oversized icon in a colored tile | major | Big icon centered in a filled rounded square/circle as hero visual or feature bullet. |
|
||||
| K5 | Floating/bobbing cards | minor | Cards over a hero that bob or float in a loop — decorative motion with no purpose. |
|
||||
| K6 | Kitchen-sink card | major | One card stacking icon-tile + category pill + tag pills + hairline divider + big price + glowy CTA. The clearest single signature. |
|
||||
| K7 | Fake macOS / app window | major | CSS-drawn window with traffic-light dots and mock UI (kanban, avatars, status pills) as hero filler. Premium pair: a detailed, fully-populated, real product UI floated with depth — but only when a product UI actually exists. |
|
||||
| K8 | Gradient pill with icon + text | major | Rounded box/pill with blue-purple fill holding an icon plus (often uppercase) label — the complete stack in one element. |
|
||||
| K9 | Default CTA button pair | major | Gradient primary with trailing arrow + glow, next to an outlined ghost ("See how it works"), same medium radius. |
|
||||
| K10 | Testimonial / quote card | major | Wide card, big quote-mark glyph, centered quote, avatar + name + title. Includes decorative oversized smart quotes around any line. |
|
||||
| K11 | Gradient-circle initials avatar | minor | Two-letter initials on a gradient circle standing in for a photo. Major when the gradient is blue-purple. |
|
||||
| K12 | Logo lockup (gradient tile + wordmark) | major | Icon in a small gradient squircle beside the name in a generic geometric font — the instant made-by-AI logo. |
|
||||
| K13 | Hairline light border on every box | minor | 1px low-opacity outline (white-on-dark / light-grey-on-light) as default card styling. Premium pair: self-colored borders + tonal elevation (an edge you feel, not see). |
|
||||
| K14 | Countdown timer | minor | DAYS/HRS/MIN/SEC boxes faking urgency whether or not anything ends. |
|
||||
| K15 | Accent-bar card | minor | Dark box with one bright line down an edge to "add interest". Premium pair: the same idea with an invented silhouette (chamfer, notch, custom bracket). |
|
||||
| K16 | Fake code-snippet window | major | Dark rounded panel, traffic lights, `quickstart.ts` tab, toy SDK call, purple-keyword/green-string palette in JetBrains Mono. |
|
||||
| K17 | Floating tag pinned to an image | minor | Small info chip ("28°C & clear") stuck top-left on an image or gradient box. |
|
||||
| K18 | Inner-glow box / pulsing live dot | minor | Bordered chip lit from inside; a status dot with an expanding glow ring. |
|
||||
| K19 | Dot under the active nav item | minor | A lone dot as active state. Premium pair: weight/color shift on the type, or a genuine sliding tab indicator. |
|
||||
| K20 | Eyebrow tick | minor | The ~30px hairline (often gradient-fading) drawn beside a kicker label to make it feel "designed". |
|
||||
| K21 | Unrounded hairline rules as decoration | minor | Square-capped lines faking structure: dividers beside paragraphs, rails down lists. |
|
||||
| K22 | Metadata as tinted pill chips, everywhere | minor | Every category/status/tag wrapped in a colored pill — component-kit dashboard, not a brand. |
|
||||
| K23 | Faked or missing logos | major/minor | Faked: invented brand marks, fake customers, uniform icon-pack rows as filler (major). Missing: no social/integration marks where real ones would earn legitimacy (minor). Real marks, one size, one quiet treatment = premium. |
|
||||
| K24 | Icon or logo in a box | minor | Any mark parked on a filled tile/chip/circle. Premium pair: the bare mark, sized and colored with intent. |
|
||||
| K25 | Botched glass | major | Blur banding/pixelation over a flat backdrop, shadow/glow leaking below, a resting halo, or blur that pops on hover. A bad blur is worse than no blur. Premium pair: real liquid glass over a backdrop worth refracting (see premium-markers.md). |
|
||||
| K26 | Grain over content | minor | A noise layer sitting on top of text, icons, or panels, muddying legibility. Grain belongs on the substrate; one deliberately masked display word is the allowed exception. |
|
||||
| K27 | AI-brand convergence kit | minor | The unexamined AI-startup identity default: orbital/orbit-ring logo mark + corporate blue + generic geometric sans (often a name ending in -AI/-ly). Cite the mark and palette actually shown; the vibe alone is not evidence. |
|
||||
|
||||
## L — Layout & Composition (Axis 4)
|
||||
|
||||
Fonts are one axis; layout is the other — a recolored skeleton is still
|
||||
slop. ≥3 majors here trigger the compounding cap (Axis 4 ≤ 40).
|
||||
|
||||
| ID | Tell | Sev | Evidence to look for |
|
||||
|----|------|-----|----------------------|
|
||||
| L1 | Default hero stack | major | Eyebrow → headline → subline → primary button + secondary link, centered down the middle. Slop layout even with fine type and color. |
|
||||
| L2 | Split hero / hero + right panel | major | Left column (kicker, big headline, subline, two buttons, stat row) + right framed visual/product panel. The skeleton is the tell, not any piece. |
|
||||
| L3 | Three-tier pricing preset | major | Free/Pro/Enterprise cards, pill over heading, "$X /mo", checkmark list, glowing "MOST POPULAR" middle card. |
|
||||
| L4 | Pre-footer CTA slab | major | Full-width rounded gradient box: centered headline, "no credit card required" byline, one dark + one light button. |
|
||||
| L5 | Kicker + serif-H2 section head | minor | Tiny uppercase accent kicker ("HOW IT WORKS") above a medium serif headline opening every section. Major when it opens 3+ sections. |
|
||||
| L6 | Small-label-over-big-heading | minor | L5 generalized past serifs: mono/uppercase label over big heading as the template for starting any section. |
|
||||
| L7 | Big serif statement block | minor | Kicker + one large serif sentence with a single italic accent word as the "philosophy" beat. |
|
||||
| L8 | Inset enquire island as default closer | minor | The rounded floated panel (kicker + serif headline + lead + form) as the closing CTA, every time. |
|
||||
| L9 | Email-pill + button form | minor | Long pill email input beside a pill button — the most repeated capture row there is. |
|
||||
| L10 | Image card with overlay caption | minor | Portrait tile, bottom gradient scrim, uppercase meta label, serif name, link arrow. |
|
||||
| L11 | Flat fill under everything after the hero | major | Atmospheric hero, then every section drops to one flat dark/cream field with boxes. The whole page needs atmosphere, not just the fold. |
|
||||
| L12 | Numbered steps beside a vertical rail | minor | 01/02/03 items along a rule. Worse with square caps (also K21). |
|
||||
| L13 | Filled + outlined button pair | major | Solid primary beside ghost secondary as the default action row, any color/radius. |
|
||||
| L14 | Standard footer | minor | Wordmark + tagline, rule, four link columns under uppercase labels, rule, copyright with a cute sign-off. Correct, expected, no idea. |
|
||||
| L15 | The SaaS meta-skeleton | major | The Stripe/Linear/Vercel clone: two-column hero → three icon-tile feature cards → tabbed switch → pricing cards → FAQ accordion → CTA slab → multi-column footer. Counts as one major AND each present block counts on its own — this is how the compounding cap fires. |
|
||||
| L16 | Hero doesn't own the fold | major | Hero shorter than the viewport with the next section peeking in unaligned; the first frame is an accident, not a composition. |
|
||||
| L17 | Content flung to far edges | minor | Two clusters jammed against opposite rims with a dead gulf between (default-asymmetric footers). Symmetry and a real grid unless asymmetry is composed. |
|
||||
| L18 | Fixed background trailing the scroll | minor | One `position: fixed` sheet dragged behind every section (and the nav) — a static texture wearing a costume. |
|
||||
| L19 | Recycling your own house style | major | The same five section shapes across briefs with a new palette — a theme reskinned, not a design. Needs portfolio context; else Unverifiable. |
|
||||
| L20 | Botched oversized footer wordmark | major | Giant brand word pasted without composition: off-center, caps clipped, gradient fighting the background, default face with no treatment. Premium pair: anchored flush to the bottom edge, above the texture, deliberate case and spacing, bleeding intentionally. |
|
||||
| L21 | Repeated section template | major | 3+ sections (or 3+ blocks in a row: identical cards with icon + heading + two lines) built on the same grid/card/column skeleton with only content, icon, or color swapped. The self-cloning is the tell, not any single block. (L15 is the page-level *sequence*; this is repetition *within* one page.) |
|
||||
|
||||
## M — Motion & Interaction (Axis 5)
|
||||
|
||||
| ID | Tell | Sev | Evidence to look for |
|
||||
|----|------|-----|----------------------|
|
||||
| M1 | Invisible-content trap | **crit** | Content starting at `opacity:0` / translated-away, revealed by JS or scroll timeline. Covers `animation-timeline: view()`, IntersectionObserver toggles, and `initial={{opacity:0}}`. If the reveal never fires, the section is GONE. Content is visible by default — absolute rule 1. |
|
||||
| M2 | Hover boop | minor | Button lifts (translateY) or scales on hover. Buttons don't move; change state cleanly (fill/color shift, icon slide). |
|
||||
| M3 | Underline-fill hover | minor | Underline that grows/wipes/travels in on links or ghost buttons. |
|
||||
| M4 | Default card hover-lift | minor | Translate-up + all-around shadow bloom + accent glowing border on every card grid. |
|
||||
| M5 | Sun-and-moon theme toggle | minor | The stock pill sliding a knob between sun and moon. |
|
||||
| M6 | Botched fill animation | major | Caps flipping sharp↔rounded mid-transition (scaleY on a rounded shape), partial fill of the track, stuttering ease. Half-built motion screams slop. |
|
||||
| M7 | Dead page | major | No authored motion at all: static nav, nothing responds to scroll or hover. "Boring/static" is a rejection on its own. Calm is allowed; dead is not. |
|
||||
| M8 | Dead controls / fake interactivity | **crit** | Tabs, accordions, toggles, or buttons that look live and do nothing when clicked — or props dressed as controls. Absolute rule 6; verify with a real click. |
|
||||
|
||||
## X — Execution & Craft (Axis 6)
|
||||
|
||||
The "broken, not designed" family. X1, X2, X3, X5, X11 are the remaining
|
||||
absolute rules.
|
||||
|
||||
| ID | Tell | Sev | Evidence to look for |
|
||||
|----|------|-----|----------------------|
|
||||
| X1 | Nothing actually centered | **crit** | Numbers floating high in circles, glyphs sitting low in tiles, labels off-axis in pills. SVG traps: `text-anchor: middle` without `dominant-baseline`, optical vs bounding-box center. Zoom in and verify. |
|
||||
| X2 | Content sliced by an edge | **crit** | Caps shaved flat, controls missing top pixels, descenders vanishing into borders — from clip-path, notches, `overflow:hidden`, fixed heights. "Clear the cut": content must sit fully inside the visible region. |
|
||||
| X3 | Ragged comparison columns | **crit** | Pricing/plan/feature columns where titles, prices, list starts, and above all buttons sit at different heights because copy length pushed rows around. Equal heights, bottom-anchored CTAs, shared baselines. |
|
||||
| X4 | Text jammed against the edge | major | Copy kissing the viewport/container rim with no gutter. (Deliberate cropped watermarks excepted.) |
|
||||
| X5 | Unreadable contrast | **crit** | Text too close in value to its background; worst on filled buttons. Every string clears its background by a real value gap. |
|
||||
| X6 | Default all-around shadow | major | Soft symmetric shadow bloomed on every side of everything by reflex — the "float it on a fluffy cloud" signature. Premium pair: tight, low-offset, directional, tinted to the surface/element — or depth from tone with no shadow. |
|
||||
| X7 | Fake shadow (second box) | major | A literal offset rectangle/duplicate element imitating a shadow to dodge a no-shadow rule — routing around the rule, worse than the shadow. |
|
||||
| X8 | Botched shadow (hard-edged box) | major | A shadow reading as a solid rounded-rectangle silhouette behind the element. If you can trace the shadow's border, it's a box, not a shadow. |
|
||||
| X9 | Bloom = blurred self-copy | major | Glow/shadow that is the element's own outline blurred and offset — a sticker with a halo, pooling to the sides, never blending. |
|
||||
| X10 | Off-center strike line | major | Strike-through/redaction bar floating off the true optical center of the glyphs (measure against real x-height). |
|
||||
| X11 | Clipped at a section overlap | **crit** | Content that should continue under an overlapping panel/sheet guillotined at the seam by the upper layer's edge or `overflow:hidden`. |
|
||||
| X12 | Cut-off glow | major | A glow clipped by a section edge so it ends in a hard line — the accidental edge on a "premium" effect. |
|
||||
| X13 | Hard image seams | major | Full-bleed image butting a flat section with a visible line. Premium fix: mask the image's own pixels with a long many-stop fade, tall section, continuous page color — never a color overlay, and never a scrim ending at the boundary. |
|
||||
| X14 | Focus states stripped | major | `outline: none` (or `:focus` suppressed) on interactive elements with no visible replacement — keyboard navigation rendered invisible. Sibling of X5: legibility laws apply to keyboard users too. Verifiable in code or by tabbing through a live page. |
|
||||
117
.github/skills/slop-eval/scripts/score.py
vendored
Normal file
117
.github/skills/slop-eval/scripts/score.py
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deterministic scoring for a slop-eval report.
|
||||
|
||||
Usage:
|
||||
score.py axis CRIT MAJOR MINOR [--cap N]
|
||||
Axis score from confirmed tell counts:
|
||||
max(0, 100 - 30*CRIT - 15*MAJOR - 5*MINOR), then min(score, cap).
|
||||
Use --cap 40 for the Layout compounding rule (>=3 major layout tells).
|
||||
|
||||
score.py overall [--cap N ...] [--fail-below N] 1:80:2 2:65:2 ... 7:NA:3
|
||||
One arg per axis, formatted axis:score:weight. Score NA (or N/A)
|
||||
excludes the axis from both sums. Caps apply to the weighted
|
||||
overall (signature gate: --cap 59; absolute-rule gate: --cap 69).
|
||||
Prints overall, Slop Index (100 - overall), and grade.
|
||||
--fail-below N exits non-zero when overall < N (CI gate).
|
||||
"""
|
||||
import sys
|
||||
|
||||
PENALTY = {"crit": 30, "major": 15, "minor": 5}
|
||||
|
||||
|
||||
def grade(score: float) -> str:
|
||||
if score >= 80:
|
||||
return "A (Premium)"
|
||||
if score >= 60:
|
||||
return "B (Considered)"
|
||||
if score >= 40:
|
||||
return "C (Generic)"
|
||||
if score >= 20:
|
||||
return "D (Slop)"
|
||||
return "F (Pure slop)"
|
||||
|
||||
|
||||
def pop_flag(args: list, flag: str, repeat: bool = False):
|
||||
vals = []
|
||||
while flag in args:
|
||||
i = args.index(flag)
|
||||
try:
|
||||
vals.append(float(args[i + 1]))
|
||||
except (IndexError, ValueError):
|
||||
sys.exit(f"{flag} requires a numeric value")
|
||||
del args[i : i + 2]
|
||||
if not repeat:
|
||||
break
|
||||
return vals
|
||||
|
||||
|
||||
def cmd_axis(args: list) -> None:
|
||||
caps = pop_flag(args, "--cap", repeat=True)
|
||||
if len(args) != 3:
|
||||
sys.exit("axis mode needs exactly: CRIT MAJOR MINOR counts")
|
||||
try:
|
||||
crit, major, minor = (int(a) for a in args)
|
||||
except ValueError:
|
||||
sys.exit("tell counts must be integers")
|
||||
if min(crit, major, minor) < 0:
|
||||
sys.exit("tell counts must be >= 0")
|
||||
score = max(
|
||||
0,
|
||||
100 - PENALTY["crit"] * crit - PENALTY["major"] * major - PENALTY["minor"] * minor,
|
||||
)
|
||||
capped = min([score] + caps)
|
||||
detail = f" (capped from {score:g})" if capped < score else ""
|
||||
print(
|
||||
f"tells: {crit} crit / {major} major / {minor} minor"
|
||||
f" -> axis score = {capped:g}{detail}"
|
||||
)
|
||||
|
||||
|
||||
def cmd_overall(args: list) -> None:
|
||||
caps = pop_flag(args, "--cap", repeat=True)
|
||||
fail_below = pop_flag(args, "--fail-below")
|
||||
if not args:
|
||||
sys.exit(__doc__)
|
||||
num = den = 0.0
|
||||
na = []
|
||||
for arg in args:
|
||||
try:
|
||||
axis, score, weight = arg.split(":")
|
||||
except ValueError:
|
||||
sys.exit(f"bad arg {arg!r}: expected axis:score:weight")
|
||||
if score.strip().upper() in ("NA", "N/A"):
|
||||
na.append(axis)
|
||||
continue
|
||||
s, w = float(score), float(weight)
|
||||
if not 0 <= s <= 100:
|
||||
sys.exit(f"axis {axis}: score {s} outside 0-100")
|
||||
num += s * w
|
||||
den += w
|
||||
if den == 0:
|
||||
sys.exit("no applicable axes")
|
||||
raw = num / den
|
||||
overall = min([raw] + caps)
|
||||
capped = f" (capped from {raw:.2f})" if overall < raw else ""
|
||||
print(f"applicable axes: {len(args) - len(na)} | NA: {', '.join(na) or 'none'}")
|
||||
print(f"sum(score x weight) = {num:g} | sum(weight) = {den:g}")
|
||||
print(f"overall = {overall:.2f}{capped}")
|
||||
print(f"Slop Index = {100 - overall:.2f}")
|
||||
print(f"grade: {grade(overall)}")
|
||||
if fail_below and overall < fail_below[0]:
|
||||
sys.exit(f"FAIL: overall {overall:.2f} below threshold {fail_below[0]:g}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
|
||||
sys.exit(__doc__)
|
||||
mode, args = sys.argv[1], sys.argv[2:]
|
||||
if mode == "axis":
|
||||
cmd_axis(args)
|
||||
elif mode == "overall":
|
||||
cmd_overall(args)
|
||||
else:
|
||||
sys.exit(f"unknown mode {mode!r}; use 'axis' or 'overall'\n\n{__doc__}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user