code-factory-4-design 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_factory_4_design-0.3.0.dist-info/METADATA +154 -0
- code_factory_4_design-0.3.0.dist-info/RECORD +19 -0
- code_factory_4_design-0.3.0.dist-info/WHEEL +5 -0
- code_factory_4_design-0.3.0.dist-info/entry_points.txt +2 -0
- code_factory_4_design-0.3.0.dist-info/licenses/LICENSE-APACHE +8 -0
- code_factory_4_design-0.3.0.dist-info/licenses/LICENSE-MIT +10 -0
- code_factory_4_design-0.3.0.dist-info/licenses/NOTICE +3 -0
- code_factory_4_design-0.3.0.dist-info/top_level.txt +1 -0
- prestige_design/__init__.py +15 -0
- prestige_design/adapters.py +28 -0
- prestige_design/attribution.py +51 -0
- prestige_design/audit.py +148 -0
- prestige_design/biases.py +119 -0
- prestige_design/cli.py +246 -0
- prestige_design/cta_hooks.py +97 -0
- prestige_design/mobile_judge.py +57 -0
- prestige_design/purpose.py +301 -0
- prestige_design/score.py +128 -0
- prestige_design/workflows.py +97 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-factory-4-design
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: The Digital Architect of Prestige โ a coding-agent skill for designing premium, high-conversion websites and mobile UIs using applied psychology and trust engineering. Ships a SKILL.md contract plus an executable design-linter that audits generated HTML/CSS against the Halo Effect, Cognitive Fluency, Trust Engineering, Peak-End Rule, and Horn-Effect triggers.
|
|
5
|
+
License-Expression: MIT OR Apache-2.0
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE-APACHE
|
|
9
|
+
License-File: LICENSE-MIT
|
|
10
|
+
License-File: NOTICE
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# The Digital Architect of Prestige ๐
|
|
16
|
+
|
|
17
|
+
A **coding-agent skill** for designing premium, high-conversion websites and
|
|
18
|
+
mobile UIs โ for Claude Code, Codex, OpenCode, Cursor, and any agent that reads
|
|
19
|
+
a project skill file. It moves the agent beyond aesthetics into **applied
|
|
20
|
+
psychology and trust engineering**, and ships an **executable design linter** so
|
|
21
|
+
the agent can *verify* its output against the principles, not just intend them.
|
|
22
|
+
|
|
23
|
+
> Users judge credibility in ~50ms from visuals alone. If it looks
|
|
24
|
+
> professional, they assume the service is too (the Halo Effect). This skill
|
|
25
|
+
> engineers that halo โ and refuses to let a pretty page ship over a broken one.
|
|
26
|
+
|
|
27
|
+
## Workflow at a glance
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
flowchart LR
|
|
31
|
+
A["User journey or page brief"] --> B["Select workflow and purpose"]
|
|
32
|
+
B --> C["Build premium UI"]
|
|
33
|
+
C --> D["Audit five laws"]
|
|
34
|
+
D --> E["Score bias, CTA, hook, mobile, and purpose fit"]
|
|
35
|
+
E -->|"fails"| F["Fix ranked recommendations"]
|
|
36
|
+
F --> D
|
|
37
|
+
E -->|"passes"| G["Ship UI with design receipt"]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Install into your agent (any OS)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python install.py # Windows / macOS / Linux (installs + verifies)
|
|
44
|
+
# then, inside your project:
|
|
45
|
+
prestige install claude # or: codex ยท opencode ยท cursor ยท generic
|
|
46
|
+
```
|
|
47
|
+
That drops `SKILL.md` where your agent auto-reads it. From then on, any
|
|
48
|
+
"design a landing page / make this premium / build a checkout" request triggers
|
|
49
|
+
the skill.
|
|
50
|
+
|
|
51
|
+
## What the agent gets
|
|
52
|
+
|
|
53
|
+
**A design contract (`SKILL.md`)** encoding five laws it applies every time:
|
|
54
|
+
|
|
55
|
+
1. **The 50ms Halo** โ hero section, one bold headline, high-fidelity cover imagery.
|
|
56
|
+
2. **Cognitive Fluency** โ Hick's/Miller's Law, extreme whitespace, one goal per section, `line-height โฅ 1.6`.
|
|
57
|
+
3. **Trust Engineering** โ security + social-proof cues at decision points, total price incl. fees upfront, guest checkout.
|
|
58
|
+
4. **Peak-End Rule** โ micro-interactions at the peaks, inline form validation, a rewarding confirmation ending.
|
|
59
|
+
5. **Horn-Effect Defense** โ one Visual DNA in CSS variables, mobile-first stability, no dated cues.
|
|
60
|
+
|
|
61
|
+
**An executable linter (`prestige audit`)** that scores generated HTML/CSS on all
|
|
62
|
+
five laws and **hard-fails on major functional flaws** โ because the skill's one
|
|
63
|
+
non-negotiable rule is *never let aesthetics mask a broken primary action or an
|
|
64
|
+
inaccessible UI.*
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
prestige scaffold site.html # premium starter (scores 100/100)
|
|
68
|
+
prestige audit site.html # score the five laws + catch Horn-Effect triggers
|
|
69
|
+
prestige purpose site.html --purpose developer # opinionated purpose-fit audit
|
|
70
|
+
prestige audit site.html --strict # exit 1 if failing โ use as an agent/CI gate
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Example on a deliberately clunky page:
|
|
74
|
+
```
|
|
75
|
+
Overall: 18/100 Verdict: NEEDS WORK
|
|
76
|
+
halo:15 fluency:20 trust:20 peak:20 horn:15
|
|
77
|
+
โ HARD FAILURES (fix before shipping โ function over aesthetics):
|
|
78
|
+
MAJOR_FLAW: primary action button has no accessible label/text.
|
|
79
|
+
โ [trust] T_NO_SECURITY: Transactional UI with no security cue near actions.
|
|
80
|
+
โ [horn] C_NO_VIEWPORT: Missing viewport meta โ mobile layout will break.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## The workflow the skill teaches
|
|
84
|
+
|
|
85
|
+
`Discovery` (audit the journey for trust gaps) โ `Build` (apply the five laws)
|
|
86
|
+
โ `Verify` (`prestige audit`, fix flags, then present). The agent treats a
|
|
87
|
+
failing audit like a failing test โ which is exactly how "premium" stops being
|
|
88
|
+
a vibe and becomes a checkable property.
|
|
89
|
+
|
|
90
|
+
## The psychology, sourced
|
|
91
|
+
Halo Effect & the 50ms window ยท Aesthetic-Usability Effect ยท Hick's & Miller's
|
|
92
|
+
Law ยท Fitts's Law ยท Peak-End Rule ยท Negativity/Horn Effect. Detailed playbooks
|
|
93
|
+
in `references/` (hero-halo, cognitive-fluency, trust-engineering, peak-end).
|
|
94
|
+
|
|
95
|
+
## v0.2 โ deeper psychology, stricter scoring, five workflows
|
|
96
|
+
|
|
97
|
+
- **Cognitive Bias Engine** โ six research-backed biases (Von Restorff, Anchoring,
|
|
98
|
+
Social Proof, Loss Aversion, Reciprocity, Cognitive Fluency) with detectors,
|
|
99
|
+
quality scoring, and precise recommendations.
|
|
100
|
+
- **CTA & Hook Verification** โ scores whether your call-to-action and headline
|
|
101
|
+
are engineered to convert (weak-verb detection, reader-focus, power words,
|
|
102
|
+
CTA-competition penalty) with exact rewrites.
|
|
103
|
+
- **Strict Mobile Judge** โ thumb-reach tap targets, viewport discipline, real
|
|
104
|
+
breakpoints, legible base text, sticky-CTA guidance โ because trust breaks on
|
|
105
|
+
mobile first.
|
|
106
|
+
- **Five Design-Principle Workflows** โ Conversion Architect, Luxury Minimalist,
|
|
107
|
+
Trust Engineer, Editorial Storyteller, Product-Led Pragmatist. Each is a
|
|
108
|
+
distinct optimization strategy that re-weights the score.
|
|
109
|
+
- **Precision Scoring** โ `prestige score <file> --workflow <key>` fuses laws +
|
|
110
|
+
biases + CTA/hooks + mobile into a 0โ100 conversion-readiness grade with
|
|
111
|
+
**recommendations ranked by conversion impact**.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
prestige workflows # see the five lenses
|
|
115
|
+
prestige score site.html --workflow trust # precise score + ranked fixes
|
|
116
|
+
prestige score site.html --strict # exit 1 if not conversion-ready
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Grounded in current research: credibility forms in ~0.05s (Google), trigger-driven
|
|
120
|
+
UX lifts conversion ~34% on average (Nielsen Norman 2024).
|
|
121
|
+
|
|
122
|
+
## v0.3 - purpose-fit design judgment
|
|
123
|
+
|
|
124
|
+
Workflows answer **what the page optimizes for**. Purpose lenses answer **what
|
|
125
|
+
psychological job the design must do for this audience and domain**.
|
|
126
|
+
|
|
127
|
+
Prestige now ships deterministic purpose-fit profiles:
|
|
128
|
+
|
|
129
|
+
- `developer` - concrete proof, docs, CLI/API clarity, GitHub/demo trust.
|
|
130
|
+
- `healthcare` - calm reassurance, privacy, clinician proof, no miracle hype.
|
|
131
|
+
- `fintech` - security, transparent fees/rates, control, compliance cues.
|
|
132
|
+
- `luxury` - restraint, craft, whitespace, fewer louder elements.
|
|
133
|
+
- `marketplace` - bilateral buyer/seller trust, reviews, protection, comparison.
|
|
134
|
+
- `saas` - product visibility, integrations, ROI, low-friction activation.
|
|
135
|
+
- `editorial` - narrative rhythm, sources, evidence, satisfying end beat.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
prestige purposes
|
|
139
|
+
prestige purpose site.html --purpose healthcare --strict
|
|
140
|
+
prestige score site.html --workflow trust --purpose healthcare
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The purpose gate scores intent clarity, proof fit, visual theme fit, action
|
|
144
|
+
language, and purpose-specific anti-patterns. This makes Prestige more
|
|
145
|
+
opinionated in the right way: not just "pretty," but fit for the decision the
|
|
146
|
+
interface exists to change.
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
Dual-licensed **Apache-2.0 OR MIT** โ pick whichever your project prefers.
|
|
150
|
+
## Criterion attribution
|
|
151
|
+
|
|
152
|
+
Prestige 0.2.1 wraps its existing five per-law scores in the factory attribution
|
|
153
|
+
envelope. Run `prestige audit page.html --json` to receive one unit per design
|
|
154
|
+
criterion, its score and threshold, and the unchanged overall verdict.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
code_factory_4_design-0.3.0.dist-info/licenses/LICENSE-APACHE,sha256=7E7Abez5cUuQccTXlM_Tcx4MlEraqeuGX67rlx1dOyQ,487
|
|
2
|
+
code_factory_4_design-0.3.0.dist-info/licenses/LICENSE-MIT,sha256=R_78kakzZsivM3qr_yf5z3A9FAz8vdBkt7J75-mrjJI,499
|
|
3
|
+
code_factory_4_design-0.3.0.dist-info/licenses/NOTICE,sha256=cj5FLrj-XYK-uwnkiggsRV62mzjmdCY6gkylrDscdoY,160
|
|
4
|
+
prestige_design/__init__.py,sha256=R1Th69fyPr-4k0hc9v2t1QxNgAz5lbGq1rfQ8xkgYK0,833
|
|
5
|
+
prestige_design/adapters.py,sha256=6lmJivNajewaFY0B4LAqvb6940G_l-PLwvSxIJ7Yj2E,1104
|
|
6
|
+
prestige_design/attribution.py,sha256=U4861YtbeHgj9iKNutAdYrRJ7xH4K95ET2InqzC1ntU,1471
|
|
7
|
+
prestige_design/audit.py,sha256=E435nynFautddK_Xf18_vOOqobS5IJaDhzl_M7ROm-U,8608
|
|
8
|
+
prestige_design/biases.py,sha256=NMQRGt4b293xn9Oc5gLaqg8iQzIoK0l4Ccc__yg5kE4,7592
|
|
9
|
+
prestige_design/cli.py,sha256=I6rI08R2Dm7ItR8EXkwxCj1VzomH2_PGTi5ELN6EUo0,11081
|
|
10
|
+
prestige_design/cta_hooks.py,sha256=jvfOPAE9DA2Rj0x17oeXwvYIG4Vg3nUrpDkrvB3-foI,4901
|
|
11
|
+
prestige_design/mobile_judge.py,sha256=pE8I771JDhN2b40CoWQZcwU3ze1gV-KgiDhLz-fVeFo,3100
|
|
12
|
+
prestige_design/purpose.py,sha256=sSje9XH-W6ZtZCJNRNVwlZfBoedVu-bqUylpc3zqjqw,13320
|
|
13
|
+
prestige_design/score.py,sha256=va8xFyKaYbQHncBznhE7iUotl2zBqLEqGomjTe3szWg,5284
|
|
14
|
+
prestige_design/workflows.py,sha256=v3yQNrYlJYgvKXBUF_24-S_HxyLNzRG4ozQBHP4bsMU,5657
|
|
15
|
+
code_factory_4_design-0.3.0.dist-info/METADATA,sha256=X4hQyLMeXQ6mHzi-wD6gbuls5jCbHd1DrQNlORRdpAQ,7540
|
|
16
|
+
code_factory_4_design-0.3.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
17
|
+
code_factory_4_design-0.3.0.dist-info/entry_points.txt,sha256=WX3IpwEGRfVagRGz_CWcwOcEngfm4-mO_dOoj63AbSA,54
|
|
18
|
+
code_factory_4_design-0.3.0.dist-info/top_level.txt,sha256=wSDpQ6UwJGuiscsml8WKqaWUchughfGx53IxsFGQQP4,16
|
|
19
|
+
code_factory_4_design-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
Distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
7
|
+
KIND. Full text: http://www.apache.org/licenses/LICENSE-2.0.txt
|
|
8
|
+
Copyright 2026 WizeMe.APP
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WizeMe.APP
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including the rights to use, copy, modify,
|
|
8
|
+
merge, publish, distribute, sublicense, and/or sell copies, subject to
|
|
9
|
+
including the above notice. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
|
|
10
|
+
OF ANY KIND. Full text: https://opensource.org/licenses/MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
prestige_design
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""The Digital Architect of Prestige โ premium web/mobile design as a skill.
|
|
2
|
+
|
|
3
|
+
Encodes five design laws as an executable audit so any coding agent (Claude
|
|
4
|
+
Code / Codex / OpenCode / Cursor / โฆ) doesn't just *know* the principles but
|
|
5
|
+
can *verify* its own output before shipping:
|
|
6
|
+
|
|
7
|
+
1. The 50ms Halo โ hero section, high-fidelity imagery, instant credibility
|
|
8
|
+
2. Cognitive Fluency โ Hick's/Miller's Law, whitespace, one goal per section
|
|
9
|
+
3. Trust Engineering โ security cues + social proof at decision points, price transparency
|
|
10
|
+
4. Peak-End Rule โ micro-interactions at peaks, a rewarding conclusion
|
|
11
|
+
5. Horn-Effect Defense โ ruthless consistency, mobile stability, no dated cues
|
|
12
|
+
|
|
13
|
+
The warning is enforced too: aesthetics never mask a MAJOR functional flaw.
|
|
14
|
+
"""
|
|
15
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Install this skill into any coding agent. The SKILL.md is the contract;
|
|
2
|
+
each agent reads it from a different location."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
def skill_text() -> str:
|
|
7
|
+
return (Path(__file__).resolve().parent/"SKILL.md").read_text()
|
|
8
|
+
|
|
9
|
+
TARGETS = {
|
|
10
|
+
"claude": [".claude/skills/prestige-design/SKILL.md"],
|
|
11
|
+
"codex": ["AGENTS.md"], # appended, not overwritten
|
|
12
|
+
"opencode": [".opencode/skills/prestige-design.md"],
|
|
13
|
+
"cursor": [".cursor/rules/prestige-design.md"],
|
|
14
|
+
"generic": ["SKILL-prestige-design.md"],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def install(root: Path, agent: str) -> list[Path]:
|
|
18
|
+
root = Path(root); created = []
|
|
19
|
+
key = {"claude-code":"claude"}.get(agent, agent)
|
|
20
|
+
text = skill_text()
|
|
21
|
+
for rel in TARGETS.get(key, ["AGENTS.md"]):
|
|
22
|
+
dst = root/rel; dst.parent.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
if dst.name == "AGENTS.md" and dst.exists():
|
|
24
|
+
dst.write_text(dst.read_text().rstrip()+"\n\n"+text)
|
|
25
|
+
else:
|
|
26
|
+
dst.write_text(text)
|
|
27
|
+
created.append(dst)
|
|
28
|
+
return created
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Attribution wrapper for Prestige's existing per-criterion scores."""
|
|
2
|
+
from dataclasses import asdict, dataclass
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FailureClass(str, Enum):
|
|
7
|
+
AMBIGUOUS_REQUIREMENT = "ambiguous_requirement"
|
|
8
|
+
UNTYPED_INPUT = "untyped_input"
|
|
9
|
+
SCOPE_ESCAPE = "scope_escape"
|
|
10
|
+
INVENTED_PARAM = "invented_param"
|
|
11
|
+
SIGNATURE_DRIFT = "signature_drift"
|
|
12
|
+
STUB_UNFILLED = "stub_unfilled"
|
|
13
|
+
COMPLEXITY_EXCEEDED = "complexity_exceeded"
|
|
14
|
+
INCONSISTENT_LOGIC = "inconsistent_logic"
|
|
15
|
+
RUNTIME_CRASH = "runtime_crash"
|
|
16
|
+
RUNTIME_TIMEOUT = "runtime_timeout"
|
|
17
|
+
WRONG_OUTPUT = "wrong_output"
|
|
18
|
+
ACCURACY_REGRESSION = "accuracy_regression"
|
|
19
|
+
NONDETERMINISM = "nondeterminism"
|
|
20
|
+
SECURITY_FINDING = "security_finding"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class UnitResult:
|
|
25
|
+
unit: str
|
|
26
|
+
stage: str
|
|
27
|
+
passed: bool
|
|
28
|
+
evidence: str
|
|
29
|
+
failure_class: FailureClass | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class Attribution:
|
|
34
|
+
stage: str
|
|
35
|
+
n_checked: int
|
|
36
|
+
n_passed: int
|
|
37
|
+
units: list[UnitResult]
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def rate(self):
|
|
41
|
+
return self.n_passed / self.n_checked if self.n_checked else 0.0
|
|
42
|
+
|
|
43
|
+
def dominant_failure_class(self):
|
|
44
|
+
return FailureClass.WRONG_OUTPUT if self.n_passed < self.n_checked else None
|
|
45
|
+
|
|
46
|
+
def to_dict(self):
|
|
47
|
+
value = asdict(self)
|
|
48
|
+
value["rate"] = self.rate
|
|
49
|
+
dominant = self.dominant_failure_class()
|
|
50
|
+
value["dominant_failure_class"] = dominant.value if dominant else None
|
|
51
|
+
return value
|
prestige_design/audit.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""The Prestige design linter โ encodes the five laws as executable checks so
|
|
2
|
+
an agent can VERIFY its output, not just intend it. Pure-stdlib HTML/CSS
|
|
3
|
+
heuristics; no browser needed. Returns per-law scores + Horn-Effect triggers +
|
|
4
|
+
hard failures for major functional flaws (the non-negotiable warning)."""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
import re
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Finding:
|
|
11
|
+
law: str
|
|
12
|
+
level: str # "pass" | "warn" | "fail" | "hard_fail"
|
|
13
|
+
code: str
|
|
14
|
+
message: str
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class AuditReport:
|
|
18
|
+
findings: list = field(default_factory=list)
|
|
19
|
+
scores: dict = field(default_factory=dict) # law -> 0..100
|
|
20
|
+
hard_failures: list = field(default_factory=list)
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def passed(self) -> bool:
|
|
24
|
+
return not self.hard_failures and all(v >= 60 for v in self.scores.values())
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def overall(self) -> int:
|
|
28
|
+
return round(sum(self.scores.values()) / len(self.scores)) if self.scores else 0
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def attribution(self):
|
|
32
|
+
from .attribution import Attribution, FailureClass, UnitResult
|
|
33
|
+
units = [
|
|
34
|
+
UnitResult(
|
|
35
|
+
unit=f"criterion:{law}",
|
|
36
|
+
stage="design_audit",
|
|
37
|
+
passed=score >= 60,
|
|
38
|
+
evidence=f"score={score}; threshold=60",
|
|
39
|
+
failure_class=None if score >= 60 else FailureClass.WRONG_OUTPUT,
|
|
40
|
+
)
|
|
41
|
+
for law, score in sorted(self.scores.items())
|
|
42
|
+
]
|
|
43
|
+
return Attribution("design_audit", len(units), sum(unit.passed for unit in units), units)
|
|
44
|
+
|
|
45
|
+
def _has(pattern, text, flags=re.I):
|
|
46
|
+
return re.search(pattern, text, flags) is not None
|
|
47
|
+
|
|
48
|
+
def _count(pattern, text, flags=re.I):
|
|
49
|
+
return len(re.findall(pattern, text, flags))
|
|
50
|
+
|
|
51
|
+
def audit_html(html: str, css: str = "") -> AuditReport:
|
|
52
|
+
"""css may be inline in html; we scan both together for style checks."""
|
|
53
|
+
blob = html + "\n" + css
|
|
54
|
+
style = css + "\n" + " ".join(re.findall(r"<style[^>]*>(.*?)</style>", html, re.S | re.I))
|
|
55
|
+
r = AuditReport()
|
|
56
|
+
f = r.findings
|
|
57
|
+
|
|
58
|
+
# ---------- LAW 1: 50ms Halo (hero) ----------
|
|
59
|
+
s = 100
|
|
60
|
+
has_hero = _has(r'class=["\'][^"\']*hero', html) or _has(r'<header', html) or _has(r'<section[^>]*hero', html)
|
|
61
|
+
if not has_hero:
|
|
62
|
+
s -= 40; f.append(Finding("halo","warn","H_NO_HERO","No identifiable hero/header section for the 50ms credibility window."))
|
|
63
|
+
if not _has(r'object-fit\s*:\s*cover', style) and not _has(r'background-size\s*:\s*cover', style):
|
|
64
|
+
s -= 20; f.append(Finding("halo","warn","H_NO_COVER_IMG","No object-fit/background cover image โ hero imagery drives the halo."))
|
|
65
|
+
h1 = _count(r'<h1', html)
|
|
66
|
+
if h1 == 0:
|
|
67
|
+
s -= 25; f.append(Finding("halo","fail","H_NO_H1","No <h1> โ the hero needs one bold headline."))
|
|
68
|
+
elif h1 > 1:
|
|
69
|
+
s -= 15; f.append(Finding("halo","warn","H_MULTI_H1",f"{h1} <h1> tags โ a single bold headline signals confidence."))
|
|
70
|
+
r.scores["halo"] = max(s, 0)
|
|
71
|
+
|
|
72
|
+
# ---------- LAW 2: Cognitive Fluency ----------
|
|
73
|
+
s = 100
|
|
74
|
+
navlinks = _count(r'<nav[^>]*>.*?</nav>', html, re.S)
|
|
75
|
+
nav_items = 0
|
|
76
|
+
for nav in re.findall(r'<nav[^>]*>(.*?)</nav>', html, re.S | re.I):
|
|
77
|
+
nav_items = max(nav_items, _count(r'<a\b', nav))
|
|
78
|
+
if nav_items > 7:
|
|
79
|
+
s -= 25; f.append(Finding("fluency","warn","F_NAV_OVERLOAD",f"Primary nav has {nav_items} items (Hick's Law: keep โค7)."))
|
|
80
|
+
if not _has(r'line-height\s*:\s*(1\.[6-9]|[2-9])', style):
|
|
81
|
+
s -= 20; f.append(Finding("fluency","warn","F_TIGHT_LEADING","Body line-height < 1.6 hurts readability/fluency."))
|
|
82
|
+
# whitespace: look for generous section padding
|
|
83
|
+
pads = re.findall(r'padding[^;:]*:\s*([0-9.]+)(rem|vw|vh|em|px)', style, re.I)
|
|
84
|
+
generous = any((u in ("rem","vw","em") and float(v) >= 3) or (u=="px" and float(v)>=48) for v,u in pads)
|
|
85
|
+
if not generous:
|
|
86
|
+
s -= 20; f.append(Finding("fluency","warn","F_NO_WHITESPACE","No generous section padding โ extreme whitespace signals premium."))
|
|
87
|
+
if not _has(r'display\s*:\s*(grid|flex)', style):
|
|
88
|
+
s -= 15; f.append(Finding("fluency","warn","F_NO_CHUNKING","No grid/flex layout โ use it to chunk info (Miller's Law)."))
|
|
89
|
+
r.scores["fluency"] = max(s, 0)
|
|
90
|
+
|
|
91
|
+
# ---------- LAW 3: Trust Engineering ----------
|
|
92
|
+
s = 100
|
|
93
|
+
trust_cues = _count(r'(secure|verified|guarantee|padlock|lock-icon|badge|ssl|encrypt|trusted)', blob)
|
|
94
|
+
social = _has(r'(review|rating|stars?|testimonial|โ
|โญ|\bfrom\s+\d+\s+reviews?)', blob)
|
|
95
|
+
is_transactional = _has(r'(checkout|buy|purchase|book now|subscribe|sign\s*up|pay\b|add to cart|price|\$\d)', blob)
|
|
96
|
+
if is_transactional:
|
|
97
|
+
if trust_cues == 0:
|
|
98
|
+
s -= 35; f.append(Finding("trust","fail","T_NO_SECURITY","Transactional UI with no security/verification cue near actions."))
|
|
99
|
+
if not social:
|
|
100
|
+
s -= 25; f.append(Finding("trust","warn","T_NO_SOCIAL_PROOF","No social proof (reviews/ratings) near decision points."))
|
|
101
|
+
# price transparency: look for fee/tax mention if a price is shown
|
|
102
|
+
if _has(r'\$\d', blob) and not _has(r'(total|incl\.|including|fees?|tax|all-in)', blob):
|
|
103
|
+
s -= 20; f.append(Finding("trust","warn","T_HIDDEN_COST","Price shown without total/fees/tax transparency (shock-factor risk)."))
|
|
104
|
+
if _has(r'(create account|register).{0,40}(required|to continue)', blob) and not _has(r'guest', blob):
|
|
105
|
+
s -= 15; f.append(Finding("trust","warn","T_FORCED_ACCOUNT","Forced account with no guest option (~26% abandonment)."))
|
|
106
|
+
else:
|
|
107
|
+
# non-transactional: trust cues optional but rewarded
|
|
108
|
+
if trust_cues == 0 and not social:
|
|
109
|
+
s -= 10; f.append(Finding("trust","warn","T_LIGHT_TRUST","Consider adding credibility cues (logos, testimonials)."))
|
|
110
|
+
r.scores["trust"] = max(s, 0)
|
|
111
|
+
|
|
112
|
+
# ---------- LAW 4: Peak-End Rule ----------
|
|
113
|
+
s = 100
|
|
114
|
+
if not _has(r'transition\s*:', style):
|
|
115
|
+
s -= 30; f.append(Finding("peak","warn","P_NO_TRANSITIONS","No CSS transitions โ micro-interactions create peaks of delight."))
|
|
116
|
+
if not _has(r':hover', style):
|
|
117
|
+
s -= 25; f.append(Finding("peak","warn","P_NO_HOVER","No :hover states โ buttons should feel satisfying to interact with."))
|
|
118
|
+
has_form = _has(r'<form|<input', html)
|
|
119
|
+
if has_form and not (_has(r':valid|:invalid', style) or _has(r'(checkmark|โ|valid)', blob)):
|
|
120
|
+
s -= 25; f.append(Finding("peak","warn","P_NO_INLINE_VALID","Form without inline validation feedback (gentle confirmation)."))
|
|
121
|
+
r.scores["peak"] = max(s, 0)
|
|
122
|
+
|
|
123
|
+
# ---------- LAW 5: Horn-Effect Defense (consistency + mobile) ----------
|
|
124
|
+
s = 100
|
|
125
|
+
if not _has(r'@media', style):
|
|
126
|
+
s -= 40; f.append(Finding("horn","fail","C_NO_RESPONSIVE","No @media query โ broken mobile is an instant unreliability signal."))
|
|
127
|
+
if not _has(r'var\(--|:root', style):
|
|
128
|
+
s -= 25; f.append(Finding("horn","warn","C_NO_CSS_VARS","No CSS variables โ hard to keep Visual DNA consistent."))
|
|
129
|
+
if not _has(r'<meta[^>]*viewport', html):
|
|
130
|
+
s -= 20; f.append(Finding("horn","fail","C_NO_VIEWPORT","Missing viewport meta โ mobile layout will break."))
|
|
131
|
+
font_families = set(re.findall(r'font-family\s*:\s*([^;]+)', style, re.I))
|
|
132
|
+
if len(font_families) > 3:
|
|
133
|
+
s -= 15; f.append(Finding("horn","warn","C_FONT_SOUP",f"{len(font_families)} distinct font-family declarations โ inconsistency risks the Horn Effect."))
|
|
134
|
+
r.scores["horn"] = max(s, 0)
|
|
135
|
+
|
|
136
|
+
# ---------- HARD FAILS: major functional flaws (the non-negotiable warning) ----------
|
|
137
|
+
# a pretty page must never ship over a broken PRIMARY action / accessibility
|
|
138
|
+
buttons = re.findall(r'<(button|a)\b[^>]*>(.*?)</\1>', html, re.S | re.I)
|
|
139
|
+
unlabeled = [b for tag,b in buttons if not re.sub(r'<[^>]+>','',b).strip() and 'aria-label' not in b]
|
|
140
|
+
if is_transactional and unlabeled:
|
|
141
|
+
r.hard_failures.append("MAJOR_FLAW: primary action button has no accessible label/text.")
|
|
142
|
+
f.append(Finding("warning","hard_fail","X_UNLABELED_CTA","A primary action is unlabeled โ major usability flaw; fix before aesthetics."))
|
|
143
|
+
imgs = re.findall(r'<img\b[^>]*>', html, re.I)
|
|
144
|
+
missing_alt = [i for i in imgs if 'alt=' not in i]
|
|
145
|
+
if len(missing_alt) >= max(3, len(imgs)//2) and imgs:
|
|
146
|
+
r.hard_failures.append(f"MAJOR_FLAW: {len(missing_alt)}/{len(imgs)} images missing alt text (accessibility).")
|
|
147
|
+
f.append(Finding("warning","hard_fail","X_NO_ALT","Widespread missing alt text โ accessibility failure blocks ship."))
|
|
148
|
+
return r
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""The Cognitive Bias Engine โ maps psychological triggers to concrete,
|
|
2
|
+
detectable interface elements. This is the depth upgrade: instead of five
|
|
3
|
+
vague 'laws', the audit checks whether specific, research-backed biases are
|
|
4
|
+
actually engaged in the markup, and scores how well each is executed.
|
|
5
|
+
|
|
6
|
+
Credibility judgments form in ~0.05s (Google Research); trigger-driven UX
|
|
7
|
+
lifts conversion ~34% on average (Nielsen Norman 2024). Each bias below has:
|
|
8
|
+
a detector (is it present?), a quality check (is it done right?), and a
|
|
9
|
+
precise recommendation (how to fix it).
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
import re
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class BiasCheck:
|
|
17
|
+
name: str
|
|
18
|
+
present: bool
|
|
19
|
+
quality: float # 0..1 โ how well executed when present
|
|
20
|
+
weight: int # conversion impact weight
|
|
21
|
+
recommendation: str = ""
|
|
22
|
+
evidence: str = ""
|
|
23
|
+
|
|
24
|
+
def _has(p, t): return re.search(p, t, re.I) is not None
|
|
25
|
+
def _count(p, t): return len(re.findall(p, t, re.I))
|
|
26
|
+
|
|
27
|
+
def check_von_restorff(html: str, css: str) -> BiasCheck:
|
|
28
|
+
"""Isolation effect: the CTA must visually stand out. Single, contrasting
|
|
29
|
+
primary button. Competing equal-weight CTAs KILL this (a documented mistake)."""
|
|
30
|
+
buttons = re.findall(r'<(?:button|a)[^>]*class=["\'][^"\']*(?:btn|button|cta)[^"\']*["\'][^>]*>', html, re.I)
|
|
31
|
+
primary_markers = _count(r'(btn-primary|cta-primary|primary|hero-cta)', html)
|
|
32
|
+
has_contrast = _has(r'(background|bg-)[^;]*(var\(--|#[0-9a-f]{3,6}|rgb)', css)
|
|
33
|
+
present = len(buttons) > 0 or _count(r'<button', html) > 0
|
|
34
|
+
# too many equal CTAs = failure of isolation
|
|
35
|
+
total_ctas = _count(r'<button|class=["\'][^"\']*(btn|cta)', html)
|
|
36
|
+
quality = 1.0
|
|
37
|
+
rec = ""
|
|
38
|
+
if total_ctas > 4:
|
|
39
|
+
quality = 0.4; rec = f"{total_ctas} button-like elements compete โ isolate ONE primary CTA with unique color/size (Von Restorff)."
|
|
40
|
+
elif primary_markers == 0 and total_ctas > 1:
|
|
41
|
+
quality = 0.6; rec = "No distinct 'primary' CTA class โ mark the single most important action so it stands out."
|
|
42
|
+
elif not has_contrast:
|
|
43
|
+
quality = 0.7; rec = "Primary CTA needs a contrasting color vs background to trigger the isolation effect."
|
|
44
|
+
return BiasCheck("Von Restorff (CTA isolation)", present, quality, 20, rec,
|
|
45
|
+
f"{total_ctas} CTA-like elements, {primary_markers} marked primary")
|
|
46
|
+
|
|
47
|
+
def check_anchoring(html: str, css: str) -> BiasCheck:
|
|
48
|
+
"""Anchoring: show a higher reference price next to the actual price so the
|
|
49
|
+
real price reads as a deal. Present only relevant on pricing UIs."""
|
|
50
|
+
has_price = _has(r'[\$ยฃโฌ]\s?\d', html)
|
|
51
|
+
if not has_price:
|
|
52
|
+
return BiasCheck("Anchoring (price)", False, 1.0, 12, "", "no pricing shown (N/A)")
|
|
53
|
+
# look for a struck-through / original / 'was' price near the active one
|
|
54
|
+
has_anchor = (_has(r'(was|originally|reg\.|<s>|<del>|line-through|strikethrough|compare)', html) or
|
|
55
|
+
_has(r'text-decoration\s*:\s*line-through', css))
|
|
56
|
+
quality = 1.0 if has_anchor else 0.3
|
|
57
|
+
rec = "" if has_anchor else "Pricing shown with no anchor โ display an original/higher reference price (e.g. 'Was $199, now $99') so the real price reads as a deal."
|
|
58
|
+
return BiasCheck("Anchoring (price)", True, quality, 12, rec,
|
|
59
|
+
"anchor present" if has_anchor else "no reference anchor")
|
|
60
|
+
|
|
61
|
+
def check_loss_aversion(html: str) -> BiasCheck:
|
|
62
|
+
"""Scarcity / urgency / FOMO: limited stock, countdowns, 'only N left'.
|
|
63
|
+
Powerful but must be genuine โ flag but reward when present."""
|
|
64
|
+
is_transactional = _has(r'(buy|checkout|purchase|book|subscribe|order|add to cart)', html)
|
|
65
|
+
if not is_transactional:
|
|
66
|
+
return BiasCheck("Loss Aversion (scarcity/urgency)", False, 1.0, 10, "", "not transactional (N/A)")
|
|
67
|
+
has_scarcity = _has(r'(only \d+[\w\s]{0,15}left|limited|selling fast|\d+ (seats?|left|spots?|in stock)|ends? (in|soon)|today only|last chance|launch pricing|hurry|\d+ people (are )?viewing)', html)
|
|
68
|
+
has_urgency = _has(r'(countdown|timer|expires?|deadline|\d+:\d+:\d+)', html)
|
|
69
|
+
present = has_scarcity or has_urgency
|
|
70
|
+
quality = 1.0 if present else 0.5
|
|
71
|
+
rec = "" if present else "Transactional page with no scarcity/urgency cue โ a genuine 'only N left' or time-limited signal engages loss aversion (use only if TRUE)."
|
|
72
|
+
return BiasCheck("Loss Aversion (scarcity/urgency)", present, quality, 10, rec,
|
|
73
|
+
"scarcity/urgency present" if present else "none")
|
|
74
|
+
|
|
75
|
+
def check_social_proof(html: str) -> BiasCheck:
|
|
76
|
+
"""Social proof near the decision point. Best: specific numbers + near CTA."""
|
|
77
|
+
has_proof = _has(r'(review|rating|testimonial|โ
|โญ|\d+[\d,]*\+? (customers|users|happy|reviews)|trusted by|as seen (in|on)|join \d)', html)
|
|
78
|
+
has_specific = _has(r'\d[\d,]*\+?\s*(customers|users|reviews|companies|teams|happy)', html)
|
|
79
|
+
has_logos = _has(r'(logo|featured|as-seen|press|forbes|techcrunch|trusted-by)', html)
|
|
80
|
+
present = has_proof
|
|
81
|
+
quality = 1.0 if (has_specific and (has_proof or has_logos)) else 0.6 if present else 0.0
|
|
82
|
+
rec = ("" if quality >= 1.0 else
|
|
83
|
+
"Add specific social proof numbers ('Join 5,000+ customers') and trust logos near the CTA." if not has_specific else
|
|
84
|
+
"Social proof present โ move it adjacent to the primary CTA for maximum effect.")
|
|
85
|
+
return BiasCheck("Social Proof", present, quality, 18, rec,
|
|
86
|
+
"specific + logos" if (has_specific and has_logos) else "present" if present else "absent")
|
|
87
|
+
|
|
88
|
+
def check_cognitive_fluency(html: str, css: str) -> BiasCheck:
|
|
89
|
+
"""Easy-to-process = trustworthy. Whitespace, hierarchy, short blocks."""
|
|
90
|
+
has_whitespace = bool(re.search(r'padding[^;:]*:\s*([3-9]|\d{2,})(rem|vw|vh|em)', css, re.I))
|
|
91
|
+
has_hierarchy = _count(r'<h[12]', html) >= 1 and _has(r'font-size', css)
|
|
92
|
+
has_generous_leading = _has(r'line-height\s*:\s*(1\.[6-9]|[2-9])', css)
|
|
93
|
+
score = sum([has_whitespace, has_hierarchy, has_generous_leading]) / 3
|
|
94
|
+
rec = ""
|
|
95
|
+
if not has_whitespace: rec = "Increase section padding (โฅ6vw) โ whitespace reads as premium and reduces cognitive load."
|
|
96
|
+
elif not has_generous_leading: rec = "Set body line-height โฅ1.6 โ easier processing reads as more trustworthy (fluency effect)."
|
|
97
|
+
return BiasCheck("Cognitive Fluency", True, score, 15, rec,
|
|
98
|
+
f"whitespace={has_whitespace}, hierarchy={has_hierarchy}, leading={has_generous_leading}")
|
|
99
|
+
|
|
100
|
+
def check_reciprocity(html: str) -> BiasCheck:
|
|
101
|
+
"""Give before you ask: free trial, free guide, value upfront."""
|
|
102
|
+
has_give = _has(r'(free|trial|no (credit card|cost)|complimentary|gift|download.{0,20}free|get started free)', html)
|
|
103
|
+
is_transactional = _has(r'(buy|subscribe|sign\s*up|checkout|book)', html)
|
|
104
|
+
if not is_transactional:
|
|
105
|
+
return BiasCheck("Reciprocity", has_give, 1.0 if has_give else 0.7, 8, "", "N/A")
|
|
106
|
+
quality = 1.0 if has_give else 0.5
|
|
107
|
+
rec = "" if has_give else "Offer value before asking โ a free trial/guide/'no credit card' cue triggers reciprocity and lowers signup friction."
|
|
108
|
+
return BiasCheck("Reciprocity", has_give, quality, 8, rec,
|
|
109
|
+
"value-first present" if has_give else "asks without giving")
|
|
110
|
+
|
|
111
|
+
def run_bias_suite(html: str, css: str) -> list[BiasCheck]:
|
|
112
|
+
return [
|
|
113
|
+
check_von_restorff(html, css),
|
|
114
|
+
check_social_proof(html),
|
|
115
|
+
check_cognitive_fluency(html, css),
|
|
116
|
+
check_anchoring(html, css),
|
|
117
|
+
check_loss_aversion(html),
|
|
118
|
+
check_reciprocity(html),
|
|
119
|
+
]
|