raven-mcp 1.12.1 → 1.13.0
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.
- package/README.md +11 -3
- package/dist/audit-consistency.d.ts +52 -0
- package/dist/audit-consistency.js +290 -0
- package/dist/audit-consistency.js.map +1 -0
- package/dist/index.js +215 -1
- package/dist/index.js.map +1 -1
- package/dist/layout-orphans.d.ts +36 -0
- package/dist/layout-orphans.js +119 -0
- package/dist/layout-orphans.js.map +1 -0
- package/dist/page-checks.js +46 -0
- package/dist/page-checks.js.map +1 -1
- package/dist/score-page.d.ts +36 -0
- package/dist/score-page.js +95 -0
- package/dist/score-page.js.map +1 -0
- package/dist/taste.d.ts +93 -0
- package/dist/taste.js +754 -0
- package/dist/taste.js.map +1 -0
- package/dist/video-playback.d.ts +61 -0
- package/dist/video-playback.js +201 -0
- package/dist/video-playback.js.map +1 -0
- package/package.json +1 -1
- package/src/data/patterns/dropdown-menu.json +150 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ A design knowledge MCP server that Claude can query when generating UI. Eight la
|
|
|
11
11
|
Raven gives Claude access to a comprehensive design knowledge base:
|
|
12
12
|
|
|
13
13
|
- **Principles** — Nielsen's 10 Heuristics, all 21 Laws of UX, Gestalt principles, WCAG accessibility, typography rules, color theory, mobile UX, D4D framework, UX writing, service design, brand, color-systems (palette-size discipline), and spacing-systems (base-unit grid + scale limits)
|
|
14
|
-
- **Patterns** — Proven UI patterns for signup flows, pricing pages, navigation, forms, landing pages, dashboards, modals, empty/error/loading states, CTAs, social proof, mobile conversion — plus content patterns (error messages, empty-state copy, notifications, form validation) and service patterns (service blueprinting, human handoff, signup-as-service, omnichannel continuity, moments of truth)
|
|
14
|
+
- **Patterns** — Proven UI patterns for signup flows, pricing pages, navigation, dropdown/select menus, forms, landing pages, dashboards, modals, empty/error/loading states, CTAs, social proof, mobile conversion — plus content patterns (error messages, empty-state copy, notifications, form validation) and service patterns (service blueprinting, human handoff, signup-as-service, omnichannel continuity, moments of truth)
|
|
15
15
|
- **Content systems** — Voice & tone guides from publicly documented brand systems: Mailchimp, GOV.UK, Shopify Polaris, and Atlassian
|
|
16
16
|
- **Research** — Qualitative, quantitative, and usability methods with do/don't protocols and checklists. Metrics frameworks: HEART, AARRR/Pirate, North Star Metric, conversion funnel, RICE, OKRs.
|
|
17
17
|
- **Service design** — Service blueprinting (with HTML blueprint generation — current vs. ideal state), human-handoff patterns, signup-as-service, omnichannel continuity, moments of truth / recovery, and the GOV.UK Service Standard
|
|
@@ -63,8 +63,9 @@ cd raven-mcp && npm install && npm run build
|
|
|
63
63
|
| `get_design_system` | Get tokens for a specific design system |
|
|
64
64
|
| `compose_system` | Mix tokens from different systems |
|
|
65
65
|
| `get_brand_system` | Get a full system styled like a well-known brand |
|
|
66
|
-
| `audit_page` | Audit HTML/CSS against Raven's quality standards — pass `html` for static audit, or `url` to render headless with optional `scroll_settle` (scroll to bottom + settle reveals) and `viewport` parameters; `containerMaxWidth` makes container checks token-aware. Pass `compact: true` to return only scores, violations, and fix_priority (drops embedded base64 screenshots) when the full payload is too large. |
|
|
67
|
-
| `
|
|
66
|
+
| `audit_page` | Audit HTML/CSS against Raven's quality standards — pass `html` for static audit, or `url` to render headless with optional `scroll_settle` (scroll to bottom + settle reveals) and `viewport` parameters; `containerMaxWidth` makes container checks token-aware. Also flags inline SVG icons that hardcode a color instead of using `currentColor`/a token. Pass `compact: true` to return only scores, violations, and fix_priority (drops embedded base64 screenshots) when the full payload is too large. |
|
|
67
|
+
| `score_page` | Return a per-category (0–10) design score for a page — typography, accessibility, spacing, color, responsive layout, design tokens, structure — derived from the same checks as `audit_page`, plus the overall score/grade, the weakest category, and categories Raven does not mechanically assess (brand, conversion, motion) |
|
|
68
|
+
| `audit_layout` | Evaluate visual rhythm, alignment, and optical balance; detects orphan-stretch (a lonely last-row grid/flex card stretching far wider than siblings) |
|
|
68
69
|
| `audit_responsive_visibility` | Render a URL at multiple breakpoints and flag content elements that are visible on desktop but hidden on mobile (display:none/opacity:0/zero-size) — categorises each as likely-oversight (content vanishing on mobile) vs intentional (decorative) |
|
|
69
70
|
| `audit_contrast` | Compute WCAG contrast ratios for every text element on a rendered page and report AA (4.5:1 / 3:1 large) and AAA pass-fail per element, with delta-to-pass for failures |
|
|
70
71
|
| `suggest_contrast_fix` | Given failing WCAG color pairs, return the minimal fg/bg change that clears the AA/AAA target — concrete passing values to fix `audit_contrast` failures |
|
|
@@ -73,6 +74,8 @@ cd raven-mcp && npm install && npm run build
|
|
|
73
74
|
| `audit_typography` | Typographic-**scale** report over rendered DOM text nodes (or a supplied snapshot) — detects the dominant modular-scale ratio and flags off-scale sizes, checks line-height consistency vs the body rhythm, and flags weight ladders >4 weights or non-standard values. Goes beyond `audit_page`'s pass/fail typography checks |
|
|
74
75
|
| `audit_tap_targets` | WCAG 2.5.5 / Apple 44pt **web** tap-target audit — enumerates every interactive element (rendered URL or snapshot) and emits a per-element fix table: selector, role, text, measured w/h, per-axis pixel deficit, and a concrete CSS fix, sorted worst-first |
|
|
75
76
|
| `audit_device_frame` | Flag cropped content in device-mockup frames — `frames` (container box + intrinsic media + object-fit, or a DevTools snippet) detects object-fit:cover crop loss when frame AR ≠ media AR; `clips` (first/last frame PNGs) detects baked-in pan/zoom (Ken Burns); `edge_frames` (PNGs) flags content truncated at a frame edge |
|
|
77
|
+
| `audit_video_playback` | Render a page and observe whether each `<video>` actually advances — samples currentTime, readyState, error codes, and autoplay-block state, then classifies each clip into playing | paused | stalled | empty | error with evidence. Catches black/non-playing videos that static frame-capture audits miss. Pass `url` to render and observe, or `dom_snapshot` for deterministic offline classification |
|
|
78
|
+
| `audit_consistency` | Corpus/multi-page audit — compares ≥2 pages and flags cross-page divergence in content-container width and hero heading tier, inferring the canonical (modal) value from the corpus when no token is supplied — catching relational defects that single-page audits miss |
|
|
76
79
|
| `audit_swiftui` | Audit SwiftUI source against Apple HIG — Dynamic Type, semantic colors, 44pt targets, 4/8pt spacing, AccentColor |
|
|
77
80
|
| `audit_ios_screen` | Score a rendered iOS screen from an accessibility/view-hierarchy snapshot — 44pt targets + contrast + rhythm, in points |
|
|
78
81
|
| `audit_ios_privacy` | Audit Info.plist (or Expo app.json) /PRIVACY.md/entitlements/source — usage-string honesty, ATS, Android permissions, bundled secrets, undisclosed default data-egress |
|
|
@@ -101,6 +104,11 @@ cd raven-mcp && npm install && npm run build
|
|
|
101
104
|
| `list_generation_jobs` | List local creative generation jobs |
|
|
102
105
|
| `plan_creative_campaign` | Plan a multi-asset campaign and optionally create draft generation jobs |
|
|
103
106
|
| `score_creative` | Score a prompt/script/concept for hook, benefit clarity, product signal, CTA, channel fit, audience fit, and brand fit |
|
|
107
|
+
| `create_taste_profile` | Create a named taste profile — a portable design-judgment ruleset (rule_id, clause, category, severity, negative prompt, owner) + precedent corpus, from explicit rules and/or a DESIGN.md-style markdown doc — persisted locally under `~/.raven/taste/` (`RAVEN_TASTE_HOME` override) |
|
|
108
|
+
| `get_taste_profile` | Load a stored taste profile's full rule catalog and precedent corpus |
|
|
109
|
+
| `list_taste_profiles` | List locally stored taste profiles with rule/corpus counts |
|
|
110
|
+
| `label_finding` | Append a human accept/revise/reject precedent to a profile's corpus — the growth loop; append-only, and accept-verdicts suppress that pattern in future audits |
|
|
111
|
+
| `audit_taste` | Judge HTML, copy text, or a live URL against a taste profile — deterministic detectors for gradients, glow/neon, second accent hue, and banned words; `owner: raven` rules route through Raven's existing page/contrast/tap-target engines; every finding cites a rule_id + concrete evidence (undetectable clauses are reported as `not_assessed`, never guessed); verdict BLOCK / WARN / PASS |
|
|
104
112
|
| `raven_reflect` | Summarize your local Raven usage log to find patterns + gaps |
|
|
105
113
|
|
|
106
114
|
## Creative studio
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type ConsistencyDimension = {
|
|
2
|
+
reference: string | null;
|
|
3
|
+
source: "token" | "modal" | "none";
|
|
4
|
+
outliers: string[];
|
|
5
|
+
};
|
|
6
|
+
export type ConsistencyIssue = {
|
|
7
|
+
severity: "warning";
|
|
8
|
+
rule: string;
|
|
9
|
+
message: string;
|
|
10
|
+
fix: string;
|
|
11
|
+
};
|
|
12
|
+
export type ConsistencyPageResult = {
|
|
13
|
+
name: string;
|
|
14
|
+
container: {
|
|
15
|
+
px: number | null;
|
|
16
|
+
classes: string[];
|
|
17
|
+
signature: string;
|
|
18
|
+
};
|
|
19
|
+
hero: {
|
|
20
|
+
size_px: number | null;
|
|
21
|
+
classes: string;
|
|
22
|
+
signature: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export type ConsistencyResult = {
|
|
26
|
+
page_count: number;
|
|
27
|
+
pages: ConsistencyPageResult[];
|
|
28
|
+
consistency: {
|
|
29
|
+
container: ConsistencyDimension;
|
|
30
|
+
hero: ConsistencyDimension;
|
|
31
|
+
};
|
|
32
|
+
issues: ConsistencyIssue[];
|
|
33
|
+
score: number;
|
|
34
|
+
grade: "A" | "B" | "C" | "D";
|
|
35
|
+
summary: string;
|
|
36
|
+
};
|
|
37
|
+
export type AuditConsistencyOptions = {
|
|
38
|
+
container_token?: number;
|
|
39
|
+
hero_token?: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Audit multiple pages for cross-page consistency of:
|
|
43
|
+
* - Content-container width (container_px / container_classes)
|
|
44
|
+
* - Hero heading tier (hero_size_px / hero_classes)
|
|
45
|
+
*
|
|
46
|
+
* Infers the canonical (modal) value from the corpus when no token is supplied.
|
|
47
|
+
* Pure — no I/O, no browser. Assumes pages.length >= 2.
|
|
48
|
+
*/
|
|
49
|
+
export declare function auditConsistency(pages: Array<{
|
|
50
|
+
name: string;
|
|
51
|
+
html: string;
|
|
52
|
+
}>, opts?: AuditConsistencyOptions): ConsistencyResult;
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// ── Cross-page consistency audit ─────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Accepts multiple pages (name + HTML string) and flags cross-page
|
|
4
|
+
// divergence in (a) content-container width and (b) hero heading tier.
|
|
5
|
+
// The canonical (reference) value for each dimension is inferred from the
|
|
6
|
+
// corpus modal when no token is supplied, so callers need not know the
|
|
7
|
+
// project's token in advance.
|
|
8
|
+
//
|
|
9
|
+
// Pure HTML-string analysis: no I/O, no browser.
|
|
10
|
+
// Reuses `containerScaleWidths` from audit-container.ts; does NOT modify it.
|
|
11
|
+
//
|
|
12
|
+
// Addresses GitHub issue #9 ("single-blob audit blind spot") — fix #1
|
|
13
|
+
// (multi-page / corpus audit mode).
|
|
14
|
+
import { containerScaleWidths } from "./audit-container.js";
|
|
15
|
+
// ── Per-page extraction helpers ──────────────────────────────────────
|
|
16
|
+
var CONTAINER_CLASS_RE = /\b(max-w-[a-z0-9.]+|container[a-z0-9-]*|w-screen|w-full)\b/gi;
|
|
17
|
+
/**
|
|
18
|
+
* Extract container-class tokens from the page HTML.
|
|
19
|
+
* Returns a sorted, unique array of class tokens matching the pattern.
|
|
20
|
+
*/
|
|
21
|
+
function extractContainerClasses(html) {
|
|
22
|
+
var found = [];
|
|
23
|
+
var m;
|
|
24
|
+
var re = new RegExp(CONTAINER_CLASS_RE.source, "gi");
|
|
25
|
+
while ((m = re.exec(html)) !== null) {
|
|
26
|
+
found.push(m[0].toLowerCase());
|
|
27
|
+
}
|
|
28
|
+
// Sort + deduplicate
|
|
29
|
+
var unique = Array.from(new Set(found)).sort();
|
|
30
|
+
return unique;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Largest declared heading font-size in px among h1/h2 CSS rules, or null.
|
|
34
|
+
* Mirrors the approach in page-checks.ts lines ~135-138: regex for h1/h2 CSS
|
|
35
|
+
* rules then pulls the font-size value.
|
|
36
|
+
*/
|
|
37
|
+
function extractHeroSizePx(html) {
|
|
38
|
+
var best = null;
|
|
39
|
+
var tags = ["h1", "h2"];
|
|
40
|
+
for (var i = 0; i < tags.length; i++) {
|
|
41
|
+
var ht = tags[i];
|
|
42
|
+
var hre = new RegExp("(?:^|[\\s,}])" + ht + "\\b[^{]*\\{[^}]*font-size\\s*:\\s*(\\d+(?:\\.\\d+)?)\\s*px", "i");
|
|
43
|
+
var hm = html.match(hre);
|
|
44
|
+
if (hm) {
|
|
45
|
+
var n = parseFloat(hm[1]);
|
|
46
|
+
if (!isNaN(n) && (best === null || n > best))
|
|
47
|
+
best = n;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return best;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extract the class attribute of the FIRST <h1 ...> tag in the page.
|
|
54
|
+
* Returns "" if none found or no class attr present.
|
|
55
|
+
*/
|
|
56
|
+
function extractFirstH1Classes(html) {
|
|
57
|
+
var m = html.match(/<h1\b([^>]*)>/i);
|
|
58
|
+
if (!m)
|
|
59
|
+
return "";
|
|
60
|
+
var attrs = m[1];
|
|
61
|
+
var classMatch = attrs.match(/\bclass\s*=\s*(?:"([^"]*?)"|'([^']*?)'|([^\s>]*))/i);
|
|
62
|
+
if (!classMatch)
|
|
63
|
+
return "";
|
|
64
|
+
return (classMatch[1] || classMatch[2] || classMatch[3] || "").trim();
|
|
65
|
+
}
|
|
66
|
+
var HERO_TYPE_SCALE_RE = /\btext-(display-[a-z0-9]+|[0-9]?xl|xs|sm|base|lg|xl)\b/i;
|
|
67
|
+
/**
|
|
68
|
+
* Derive the hero signature for a page.
|
|
69
|
+
* If hero_size_px is known, use that as the signature (e.g. "64").
|
|
70
|
+
* Else find the first type-scale class in hero_classes.
|
|
71
|
+
* Else "".
|
|
72
|
+
*/
|
|
73
|
+
function heroSignature(heroPx, heroClasses) {
|
|
74
|
+
if (heroPx !== null)
|
|
75
|
+
return String(heroPx);
|
|
76
|
+
var m = heroClasses.match(HERO_TYPE_SCALE_RE);
|
|
77
|
+
if (m)
|
|
78
|
+
return m[0];
|
|
79
|
+
return "";
|
|
80
|
+
}
|
|
81
|
+
// ── Per-page extraction ───────────────────────────────────────────────
|
|
82
|
+
function extractPage(name, html) {
|
|
83
|
+
// Container
|
|
84
|
+
var widths = containerScaleWidths(html);
|
|
85
|
+
var containerPx = widths.length > 0 ? Math.max.apply(null, widths) : null;
|
|
86
|
+
var containerClasses = extractContainerClasses(html);
|
|
87
|
+
var containerSignature;
|
|
88
|
+
if (containerPx !== null) {
|
|
89
|
+
containerSignature = String(containerPx);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
containerSignature = containerClasses.join(" ");
|
|
93
|
+
}
|
|
94
|
+
// Hero
|
|
95
|
+
var heroPx = extractHeroSizePx(html);
|
|
96
|
+
var heroClasses = extractFirstH1Classes(html);
|
|
97
|
+
var herSig = heroSignature(heroPx, heroClasses);
|
|
98
|
+
return {
|
|
99
|
+
name: name,
|
|
100
|
+
container: {
|
|
101
|
+
px: containerPx,
|
|
102
|
+
classes: containerClasses,
|
|
103
|
+
signature: containerSignature
|
|
104
|
+
},
|
|
105
|
+
hero: {
|
|
106
|
+
size_px: heroPx,
|
|
107
|
+
classes: heroClasses,
|
|
108
|
+
signature: herSig
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// ── Modal inference ───────────────────────────────────────────────────
|
|
113
|
+
/**
|
|
114
|
+
* Find the modal (most frequent) value among non-empty strings.
|
|
115
|
+
* Returns null if all are empty, or if there's a tie among ≥2 distinct values.
|
|
116
|
+
*/
|
|
117
|
+
function findModal(values) {
|
|
118
|
+
var nonEmpty = values.filter(function (v) { return v !== ""; });
|
|
119
|
+
if (nonEmpty.length === 0)
|
|
120
|
+
return null;
|
|
121
|
+
var counts = {};
|
|
122
|
+
for (var i = 0; i < nonEmpty.length; i++) {
|
|
123
|
+
var v = nonEmpty[i];
|
|
124
|
+
counts[v] = (counts[v] || 0) + 1;
|
|
125
|
+
}
|
|
126
|
+
var maxCount = 0;
|
|
127
|
+
var keys = Object.keys(counts);
|
|
128
|
+
for (var k = 0; k < keys.length; k++) {
|
|
129
|
+
if (counts[keys[k]] > maxCount)
|
|
130
|
+
maxCount = counts[keys[k]];
|
|
131
|
+
}
|
|
132
|
+
// All values with the max count
|
|
133
|
+
var leaders = keys.filter(function (k) { return counts[k] === maxCount; });
|
|
134
|
+
// Tie among ≥2 distinct values → no modal
|
|
135
|
+
if (leaders.length >= 2)
|
|
136
|
+
return null;
|
|
137
|
+
return leaders[0];
|
|
138
|
+
}
|
|
139
|
+
function checkDimension(pageNames, signatures, token) {
|
|
140
|
+
// All non-empty signatures
|
|
141
|
+
var nonEmpty = signatures.filter(function (s) { return s !== ""; });
|
|
142
|
+
// 1. Token override
|
|
143
|
+
if (token !== undefined && token !== null) {
|
|
144
|
+
var ref = String(token);
|
|
145
|
+
var outliers = [];
|
|
146
|
+
for (var i = 0; i < signatures.length; i++) {
|
|
147
|
+
var sig = signatures[i];
|
|
148
|
+
if (sig !== "" && sig !== ref) {
|
|
149
|
+
outliers.push(pageNames[i]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { reference: ref, source: "token", outliers: outliers };
|
|
153
|
+
}
|
|
154
|
+
// 2. All empty → source "none"
|
|
155
|
+
if (nonEmpty.length === 0) {
|
|
156
|
+
return { reference: null, source: "none", outliers: [] };
|
|
157
|
+
}
|
|
158
|
+
// 3. Modal inference
|
|
159
|
+
var modal = findModal(signatures);
|
|
160
|
+
if (modal !== null) {
|
|
161
|
+
// Clear modal: outliers are non-empty pages that differ
|
|
162
|
+
var outliers2 = [];
|
|
163
|
+
for (var j = 0; j < signatures.length; j++) {
|
|
164
|
+
if (signatures[j] !== "" && signatures[j] !== modal) {
|
|
165
|
+
outliers2.push(pageNames[j]);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { reference: modal, source: "modal", outliers: outliers2 };
|
|
169
|
+
}
|
|
170
|
+
// 4. Tie (no modal): emit all non-empty differing pages as outliers.
|
|
171
|
+
// reference is null (can't declare canonical without a token), so source is
|
|
172
|
+
// "none" — "modal" always carries a non-null reference, keeping the two fields
|
|
173
|
+
// internally consistent for consumers.
|
|
174
|
+
var allOutliers = [];
|
|
175
|
+
for (var m = 0; m < signatures.length; m++) {
|
|
176
|
+
if (signatures[m] !== "") {
|
|
177
|
+
allOutliers.push(pageNames[m]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { reference: null, source: "none", outliers: allOutliers };
|
|
181
|
+
}
|
|
182
|
+
// ── Issue generation ──────────────────────────────────────────────────
|
|
183
|
+
function makeContainerIssue(outliers, reference) {
|
|
184
|
+
// Reference may be a numeric px width ("1152") or a class signature ("container-wide");
|
|
185
|
+
// only suffix "px" for the numeric form.
|
|
186
|
+
var refIsNumeric = reference !== null && /^\d+(?:\.\d+)?$/.test(reference);
|
|
187
|
+
var refStr = reference === null ? "(no modal — all values diverge)" : (refIsNumeric ? reference + "px" : reference);
|
|
188
|
+
var pageList = outliers.join(", ");
|
|
189
|
+
return {
|
|
190
|
+
severity: "warning",
|
|
191
|
+
rule: "consistency/container-width",
|
|
192
|
+
message: "Container-width inconsistency across pages. Reference: " +
|
|
193
|
+
refStr +
|
|
194
|
+
". Divergent page(s): " +
|
|
195
|
+
pageList +
|
|
196
|
+
".",
|
|
197
|
+
fix: "Align all pages to the same container token (" +
|
|
198
|
+
(reference === null ? "the project canonical width" : refStr) +
|
|
199
|
+
"). Use a shared layout wrapper so the container is defined once, not per-page."
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function makeHeroIssue(outliers, reference) {
|
|
203
|
+
var refStr = reference !== null ? reference : "(no modal — all values diverge)";
|
|
204
|
+
var pageList = outliers.join(", ");
|
|
205
|
+
return {
|
|
206
|
+
severity: "warning",
|
|
207
|
+
rule: "consistency/hero-tier",
|
|
208
|
+
message: "Hero heading tier inconsistency across pages. Reference: " +
|
|
209
|
+
refStr +
|
|
210
|
+
". Divergent page(s): " +
|
|
211
|
+
pageList +
|
|
212
|
+
".",
|
|
213
|
+
fix: "Use the same hero heading class/size (" +
|
|
214
|
+
(reference !== null ? reference : "define a canonical tier") +
|
|
215
|
+
") for the top-level heading on every page. If a page intentionally uses a smaller heading, document the exception in the design system."
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Audit multiple pages for cross-page consistency of:
|
|
220
|
+
* - Content-container width (container_px / container_classes)
|
|
221
|
+
* - Hero heading tier (hero_size_px / hero_classes)
|
|
222
|
+
*
|
|
223
|
+
* Infers the canonical (modal) value from the corpus when no token is supplied.
|
|
224
|
+
* Pure — no I/O, no browser. Assumes pages.length >= 2.
|
|
225
|
+
*/
|
|
226
|
+
export function auditConsistency(pages, opts) {
|
|
227
|
+
var options = opts || {};
|
|
228
|
+
// Extract per-page data
|
|
229
|
+
var pageResults = pages.map(function (p) {
|
|
230
|
+
return extractPage(p.name, p.html);
|
|
231
|
+
});
|
|
232
|
+
var pageNames = pageResults.map(function (p) { return p.name; });
|
|
233
|
+
var containerSigs = pageResults.map(function (p) { return p.container.signature; });
|
|
234
|
+
var heroSigs = pageResults.map(function (p) { return p.hero.signature; });
|
|
235
|
+
// Consistency checks
|
|
236
|
+
var containerDim = checkDimension(pageNames, containerSigs, options.container_token);
|
|
237
|
+
var heroDim = checkDimension(pageNames, heroSigs, options.hero_token);
|
|
238
|
+
// Issues
|
|
239
|
+
var issues = [];
|
|
240
|
+
if (containerDim.outliers.length > 0) {
|
|
241
|
+
issues.push(makeContainerIssue(containerDim.outliers, containerDim.reference));
|
|
242
|
+
}
|
|
243
|
+
if (heroDim.outliers.length > 0) {
|
|
244
|
+
issues.push(makeHeroIssue(heroDim.outliers, heroDim.reference));
|
|
245
|
+
}
|
|
246
|
+
// Scoring
|
|
247
|
+
var failCount = (containerDim.outliers.length > 0 ? 1 : 0) +
|
|
248
|
+
(heroDim.outliers.length > 0 ? 1 : 0);
|
|
249
|
+
var score = Math.round(((2 - failCount) / 2) * 100);
|
|
250
|
+
var grade = failCount === 0 ? "A" : failCount === 1 ? "C" : "D";
|
|
251
|
+
// Summary
|
|
252
|
+
var summary;
|
|
253
|
+
if (failCount === 0) {
|
|
254
|
+
summary =
|
|
255
|
+
"All " +
|
|
256
|
+
pages.length +
|
|
257
|
+
" pages are consistent on container width and hero heading tier. Score: " +
|
|
258
|
+
score +
|
|
259
|
+
"/100 (A).";
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
var dims = [];
|
|
263
|
+
if (containerDim.outliers.length > 0)
|
|
264
|
+
dims.push("container width");
|
|
265
|
+
if (heroDim.outliers.length > 0)
|
|
266
|
+
dims.push("hero heading tier");
|
|
267
|
+
summary =
|
|
268
|
+
pages.length +
|
|
269
|
+
" pages audited. Inconsistency detected in: " +
|
|
270
|
+
dims.join(", ") +
|
|
271
|
+
". Score: " +
|
|
272
|
+
score +
|
|
273
|
+
"/100 (" +
|
|
274
|
+
grade +
|
|
275
|
+
").";
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
page_count: pages.length,
|
|
279
|
+
pages: pageResults,
|
|
280
|
+
consistency: {
|
|
281
|
+
container: containerDim,
|
|
282
|
+
hero: heroDim
|
|
283
|
+
},
|
|
284
|
+
issues: issues,
|
|
285
|
+
score: score,
|
|
286
|
+
grade: grade,
|
|
287
|
+
summary: summary
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=audit-consistency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-consistency.js","sourceRoot":"","sources":["../src/audit-consistency.ts"],"names":[],"mappings":"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"}
|