Runs / statement Batch Runs from different design-system versions should not be compared.
The page A screenshot in each theme, as the benchmark took it.
page.tsx As the agent left it; marked lines broke a rule.
Violations 140 counted, under 9 rules. “Not counted” marks a compiler error that restates a design-system violation, “Ignored” one the agent silenced with a comment. The first 200 are listed.
Steps One row per model call, with the tools it asked for. An agent that reads the guidelines before writing shows it here; what a checker or submit_view answered is under its call.
Step Tools Tokens in Tokens out Cost 0 list_files 784 36 $0.0003 1 read_file 322 51 $0.0002 2 read_file 845 689 $0.0011 3
Output cap The provider's default 2
3
4 type TransactionStatus = "pending" | "booked";
5
6 type Transaction = {
7 id: string;
8 date: string;
9 description: string;
10 category: string;
11 amount: number;
12 status: TransactionStatus;
13 };
14
15 const account = data.account;
16 const period = account.period;
17 const currency = account.currency;
18
19 const allTransactions = data.transactions as ReadonlyArray<Transaction>;
20
21 /* ------------------------------------------------------------------ *
22 * Formatting helpers
23 * ------------------------------------------------------------------ */
24
25 const currencyFormat = new Intl.NumberFormat("en-GB", {
26 style: "currency",
27 currency,
28 minimumFractionDigits: 2,
29 maximumFractionDigits: 2,
30 });
31
32 const shortDateFormat = new Intl.DateTimeFormat("en-GB", {
33 day: "2-digit",
34 month: "short",
35 year: "numeric",
36 timeZone: "UTC",
37 });
38
39 const longDateFormat = new Intl.DateTimeFormat("en-GB", {
40 day: "numeric",
41 month: "long",
42 year: "numeric",
43 timeZone: "UTC",
44 });
45
46 const monthFormat = new Intl.DateTimeFormat("en-GB", {
47 month: "long",
48 year: "numeric",
49 timeZone: "UTC",
50 });
51
52 function toCents(value: number): number {
53 return Math.round(value * 100);
54 }
55
56 function fromCents(cents: number): number {
57 return cents / 100;
58 }
59
60 /** Plain amount, no sign (used for the balance). */
61 function formatMoney(value: number): string {
62 return currencyFormat.format(fromCents(toCents(value)));
63 }
64
65 /** Always signed: "+€2,840.00" for money in, "-€86.42" for money out. */
66 function formatSignedMoney(value: number): string {
67 const cents = toCents(value);
68 const formatted = currencyFormat.format(Math.abs(fromCents(cents)));
69 if (cents < 0) return `-${formatted}`;
70 if (cents > 0) return `+${formatted}`;
71 return formatted;
72 }
73
74 /**
75 * Parses a "YYYY-MM-DD" string as a calendar date (UTC midnight) so that the
76 * rendered day never shifts depending on the viewer's time zone.
77 */
78 function parseIsoDate(iso: string): Date | null {
79 const parts = iso.split("-");
80 if (parts.length !== 3) return null;
81 const [year, month, day] = parts.map(Number) as number[];
82 if (
83 !Number.isFinite(year) ||
84 !Number.isFinite(month) ||
85 !Number.isFinite(day) ||
86 month < 1 ||
87 month > 12 ||
88 day < 1 ||
89 day > 31
90 ) {
91 return null;
92 }
93 return new Date(Date.UTC(year, month - 1, day));
94 }
95
96 function formatShortDate(iso: string): string {
97 const date = parseIsoDate(iso);
98 return date ? shortDateFormat.format(date) : iso;
99 }
100
101 function formatLongDate(iso: string): string {
102 const date = parseIsoDate(iso);
103 return date ? longDateFormat.format(date) : iso;
104 }
105
106 function formatMonth(iso: string): string {
107 const date = parseIsoDate(iso);
108 return date ? monthFormat.format(date) : iso;
109 }
110
111 /* ------------------------------------------------------------------ *
112 * Styles
113 * ------------------------------------------------------------------ */
114
115 const styles = {
116 page: css({
117 minHeight: "100vh",
118 background: "#f4f5f7",
119 color: "#111827",
120 padding: { base: "20px 16px 40px", md: "44px 24px 64px" },
121 fontFamily:
122 'Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
123 WebkitFontSmoothing: "antialiased",
124 }),
125 container: css({
126 maxWidth: "880px",
127 margin: "0 auto",
128 display: "flex",
129 flexDirection: "column",
130 gap: "20px",
131 }),
132 eyebrow: css({
133 margin: "0",
134 fontSize: "12px",
135 fontWeight: 700,
136 letterSpacing: "0.12em",
137 textTransform: "uppercase",
138 color: "#6b7280",
139 }),
140 title: css({
141 margin: "6px 0 0",
142 fontSize: { base: "26px", md: "32px" },
143 lineHeight: 1.15,
144 fontWeight: 700,
145 letterSpacing: "-0.01em",
146 color: "#0f172a",
147 }),
148 periodLine: css({
149 margin: "8px 0 0",
150 fontSize: "14px",
151 color: "#6b7280",
152 }),
153 card: css({
154 background: "#ffffff",
155 border: "1px solid #e5e7eb",
156 borderRadius: "16px",
157 boxShadow: "0 1px 2px rgba(16, 24, 40, 0.04), 0 10px 28px rgba(16, 24, 40, 0.05)",
158 }),
159 summaryCard: css({
160 display: "flex",
161 flexWrap: "wrap",
162 gap: "24px",
163 justifyContent: "space-between",
164 alignItems: "flex-end",
165 padding: { base: "20px", md: "24px" },
166 }),
167 label: css({
168 display: "block",
169 fontSize: "11px",
170 fontWeight: 700,
171 letterSpacing: "0.1em",
172 textTransform: "uppercase",
173 color: "#6b7280",
174 }),
175 holder: css({
176 margin: "6px 0 0",
177 fontSize: "20px",
178 fontWeight: 650,
179 color: "#0f172a",
180 }),
181 ibanLabel: css({
182 display: "block",
183 marginTop: "16px",
184 fontSize: "11px",
185 fontWeight: 700,
186 letterSpacing: "0.1em",
187 textTransform: "uppercase",
188 color: "#6b7280",
189 }),
190 iban: css({
191 margin: "6px 0 0",
192 fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace',
193 fontSize: "14px",
194 letterSpacing: "0.045em",
195 color: "#374151",
196 wordBreak: "break-word",
197 }),
198 balanceBlock: css({
199 textAlign: "right",
200 minWidth: "180px",
201 }),
202 balance: css({
203 margin: "6px 0 0",
204 fontSize: { base: "28px", md: "32px" },
205 lineHeight: 1.1,
206 fontWeight: 700,
207 letterSpacing: "-0.02em",
208 color: "#0f172a",
209 fontVariantNumeric: "tabular-nums",
210 }),
211 balanceNote: css({
212 margin: "8px 0 0",
213 fontSize: "12.5px",
214 color: "#6b7280",
215 }),
216 stats: css({
217 display: "grid",
218 gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))",
219 gap: "12px",
220 }),
221 stat: css({
222 padding: "16px",
223 background: "#ffffff",
224 border: "1px solid #e5e7eb",
225 borderRadius: "12px",
226 }),
227 statLabel: css({
228 display: "block",
229 fontSize: "11px",
230 fontWeight: 700,
231 letterSpacing: "0.1em",
232 textTransform: "uppercase",
233 color: "#6b7280",
234 }),
235 statValue: css({
236 margin: "6px 0 0",
237 fontSize: "18px",
238 fontWeight: 650,
239 fontVariantNumeric: "tabular-nums",
240 color: "#0f172a",
241 }),
242 statValueIn: css({
243 color: "#15803d",
244 }),
245 statValueOut: css({
246 color: "#b42318",
247 }),
248 statValuePending: css({
249 color: "#92400e",
250 }),
251 sectionHeader: css({
252 display: "flex",
253 flexWrap: "wrap",
254 alignItems: "center",
255 justifyContent: "space-between",
256 gap: "8px",
257 padding: "18px 20px",
258 borderBottom: "1px solid #eceef1",
259 }),
260 sectionTitle: css({
261 margin: "0",
262 fontSize: "16px",
263 fontWeight: 700,
264 color: "#0f172a",
265 }),
266 sectionMeta: css({
267 margin: "0",
268 fontSize: "13px",
269 color: "#6b7280",
270 }),
271 tableWrap: css({
272 overflowX: "auto",
273 }),
274 table: css({
275 width: "100%",
276 minWidth: "560px",
277 borderCollapse: "collapse",
278 fontSize: "14px",
279 }),
280 th: css({
281 padding: "10px 16px",
282 textAlign: "left",
283 fontSize: "11px",
284 fontWeight: 700,
285 letterSpacing: "0.1em",
286 textTransform: "uppercase",
287 color: "#6b7280",
288 background: "#fafbfc",
289 borderBottom: "1px solid #eceef1",
290 whiteSpace: "nowrap",
291 }),
292 thAmount: css({
293 textAlign: "right",
294 }),
295 row: css({
296 "& td": {
297 padding: "14px 16px",
298 borderBottom: "1px solid #f2f3f5",
299 verticalAlign: "top",
300 background: "#ffffff",
301 },
302 "&:last-child td": {
303 borderBottom: "none",
304 },
305 "&:hover td": {
306 background: "#f9fafb",
307 },
308 "&[data-pending='true'] td, &[data-pending='true']:hover td": {
309 background: "#fffaeb",
310 },
311 "&[data-pending='true'] td:first-of-type": {
312 boxShadow: "inset 3px 0 0 0 #d97706",
313 },
314 }),
315 cellDate: css({
316 color: "#4b5563",
317 fontSize: "13px",
318 whiteSpace: "nowrap",
319 fontVariantNumeric: "tabular-nums",
320 }),
321 descriptionCell: css({
322 display: "flex",
323 flexWrap: "wrap",
324 alignItems: "center",
325 gap: "8px",
326 }),
327 description: css({
328 fontWeight: 500,
329 color: "#111827",
330 }),
331 pendingBadge: css({
332 display: "inline-block",
333 padding: "1px 8px",
334 borderRadius: "999px",
335 background: "#fef3c7",
336 border: "1px solid #fcd34d",
337 color: "#92400e",
338 fontSize: "11px",
339 fontWeight: 700,
340 letterSpacing: "0.06em",
341 textTransform: "uppercase",
342 whiteSpace: "nowrap",
343 }),
344 categoryChip: css({
345 display: "inline-block",
346 padding: "2px 10px",
347 borderRadius: "999px",
348 background: "#f3f4f6",
349 color: "#374151",
350 fontSize: "12px",
351 whiteSpace: "nowrap",
352 }),
353 cellAmount: css({
354 textAlign: "right",
355 whiteSpace: "nowrap",
356 fontVariantNumeric: "tabular-nums",
357 fontWeight: 650,
358 }),
359 amountOut: css({
360 color: "#b42318",
361 }),
362 amountIn: css({
363 color: "#15803d",
364 }),
365 footnote: css({
366 margin: "14px 2px 0",
367 fontSize: "12.5px",
368 lineHeight: 1.6,
369 color: "#6b7280",
370 }),
371 srOnly: css({
372 position: "absolute",
373 width: "1px",
374 height: "1px",
375 padding: "0",
376 margin: "-1px",
377 overflow: "hidden",
378 clip: "rect(0, 0, 0, 0)",
379 whiteSpace: "nowrap",
380 border: "0",
381 }),
382 };
383
384 /* ------------------------------------------------------------------ *
385 * Page
386 * ------------------------------------------------------------------ */
387
388 export default function OctoberStatementPage() {
389 // Newest first; the sort is stable, so same-day items keep their order.
390 const transactions = [...allTransactions].sort((a, b) => {
391 if (a.date === b.date) return 0;
392 return a.date < b.date ? 1 : -1;
393 });
394
395 const pendingTransactions = transactions.filter((tx) => tx.status === "pending");
396 const bookedTransactions = transactions.filter((tx) => tx.status !== "pending");
397
398 const moneyInCents = bookedTransactions.reduce(
399 (sum, tx) => (tx.amount > 0 ? sum + toCents(tx.amount) : sum),
400 0,
401 );
402 const moneyOutCents = bookedTransactions.reduce(
403 (sum, tx) => (tx.amount < 0 ? sum + toCents(tx.amount) : sum),
404 0,
405 );
406 const pendingCents = pendingTransactions.reduce((sum, tx) => sum + toCents(tx.amount), 0);
407
408 const pendingCount = pendingTransactions.length;
409 const monthLabel = formatMonth(period.from);
410
411 return (
412 <main className={styles.page}>
413 <div className={styles.container}>
414 <header>
415 <p className={styles.eyebrow}>Current account statement</p>
416 <h1 className={styles.title}>{monthLabel} statement</h1>
417 <p className={styles.periodLine}>
418 {formatLongDate(period.from)} — {formatLongDate(period.to)} · Amounts in {currency}
419 </p>
420 </header>
421
422 <section
423 className={`${styles.card} ${styles.summaryCard}`}
424 aria-label="Account summary"
425 >
426 <div>
427 <span className={styles.label}>Account holder</span>
428 <p className={styles.holder}>{account.holder}</p>
429 <span className={styles.ibanLabel}>IBAN</span>
430 <p className={styles.iban}>{account.iban}</p>
431 </div>
432
433 <div className={styles.balanceBlock}>
434 <span className={styles.label}>Current balance</span>
435 <p className={styles.balance}>{formatMoney(account.balance)}</p>
436 {pendingCount > 0 ? (
437 <p className={styles.balanceNote}>
438 {pendingCount} transaction{pendingCount === 1 ? "" : "s"} still pending
439 </p>
440 ) : null}
441 </div>
442 </section>
443
444 <section className={styles.stats} aria-label="Period totals">
445 <div className={styles.stat}>
446 <span className={styles.statLabel}>Money in (booked)</span>
447 <p className={`${styles.statValue} ${styles.statValueIn}`}>
448 {formatSignedMoney(fromCents(moneyInCents))}
449 </p>
450 </div>
451 <div className={styles.stat}>
452 <span className={styles.statLabel}>Money out (booked)</span>
453 <p className={`${styles.statValue} ${styles.statValueOut}`}>
454 {formatSignedMoney(fromCents(moneyOutCents))}
455 </p>
456 </div>
457 <div className={styles.stat}>
458 <span className={styles.statLabel}>Pending ({pendingCount})</span>
459 <p className={`${styles.statValue} ${styles.statValuePending}`}>
460 {formatSignedMoney(fromCents(pendingCents))}
461 </p>
462 </div>
463 </section>
464
465 <section className={styles.card} aria-labelledby="transactions-heading">
466 <div className={styles.sectionHeader}>
467 <h2 id="transactions-heading" className={styles.sectionTitle}>
468 Transactions
469 </h2>
470 <p className={styles.sectionMeta}>
471 {transactions.length} transaction{transactions.length === 1 ? "" : "s"}
472 {pendingCount > 0 ? ` · ${pendingCount} pending` : ""}
473 </p>
474 </div>
475
476 <div className={styles.tableWrap}>
477 <table className={styles.table}>
478 <caption className={styles.srOnly}>
479 Transactions from {formatLongDate(period.from)} to {formatLongDate(period.to)} on
480 account {account.iban}
481 </caption>
482 <thead>
483 <tr>
484 <th scope="col" className={styles.th}>
485 Date
486 </th>
487 <th scope="col" className={styles.th}>
488 Description
489 </th>
490 <th scope="col" className={styles.th}>
491 Category
492 </th>
493 <th scope="col" className={`${styles.th} ${styles.thAmount}`}>
494 Amount ({currency})
495 </th>
496 </tr>
497 </thead>
498 <tbody>
499 {transactions.map((tx) => {
500 const isPending = tx.status === "pending";
501
502 return (
503 <tr
504 key={tx.id}
505 className={styles.row}
506 data-pending={isPending ? "true" : "false"}
507 data-status={tx.status}
508 >
509 <td className={styles.cellDate}>
510 <time dateTime={tx.date}>{formatShortDate(tx.date)}</time>
511 </td>
512 <td>
513 <div className={styles.descriptionCell}>
514 <span className={styles.description}>{tx.description}</span>
515 {isPending ? <span className={styles.pendingBadge}>Pending</span> : null}
516 </div>
517 </td>
518 <td>
519 <span className={styles.categoryChip}>{tx.category}</span>
520 </td>
521 <td
522 className={`${styles.cellAmount} ${
523 tx.amount < 0 ? styles.amountOut : styles.amountIn
524 }`}
525 >
526 {formatSignedMoney(tx.amount)}
527 </td>
528 </tr>
529 );
530 })}
531 </tbody>
532 </table>
533 </div>
534 </section>
535
536 <p className={styles.footnote}>
537 Pending transactions are not final yet and may still change; they are excluded from the
538 booked totals above. All amounts are in {currency}.
539 </p>
540 </div>
541 </main>
542 );
543 }
544
`0.12em` is a raw length. Use a token.
raw-values/raw-color 34 design-system page.tsx:118:17 · #f4f5f7 `#f4f5f7` is a raw color. Use a semantic color token. page.tsx:119:12 · #111827 `#111827` is a raw color. Use a semantic color token. page.tsx:138:12 · #6b7280 `#6b7280` is a raw color. Use a semantic color token. page.tsx:146:12 · #0f172a `#0f172a` is a raw color. Use a semantic color token. page.tsx:151:12 · #6b7280 `#6b7280` is a raw color. Use a semantic color token. page.tsx:154:17 · #ffffff `#ffffff` is a raw color. Use a semantic color token. page.tsx:155:13 · 1px solid #e5e7eb `1px solid #e5e7eb` is a raw color. Use a semantic color token. page.tsx:157:16 · 0 1px 2px rgba(16, 24, 40, 0.04), 0 10px 28px rgba(16, 24, 40, 0.05) `0 1px 2px rgba(16, 24, 40, 0.04), 0 10px 28px rgba(16, 24, 40, 0.05)` is a raw color. Use a semantic color token. page.tsx:173:12 · #6b7280 `#6b7280` is a raw color. Use a semantic color token. page.tsx:179:12 · #0f172a `#0f172a` is a raw color. Use a semantic color token. page.tsx:188:12 · #6b7280 `#6b7280` is a raw color. Use a semantic color token. page.tsx:195:12 · #374151 `#374151` is a raw color. Use a semantic color token. page.tsx:208:12 · #0f172a `#0f172a` is a raw color. Use a semantic color token. page.tsx:214:12 · #6b7280 `#6b7280` is a raw color. Use a semantic color token. page.tsx:223:17 · #ffffff `#ffffff` is a raw color. Use a semantic color token. page.tsx:224:13 · 1px solid #e5e7eb `1px solid #e5e7eb` is a raw color. Use a semantic color token. page.tsx:233:12 · #6b7280 `#6b7280` is a raw color. Use a semantic color token. page.tsx:240:12 · #0f172a `#0f172a` is a raw color. Use a semantic color token. page.tsx:243:12 · #15803d `#15803d` is a raw color. Use a semantic color token. page.tsx:246:12 · #b42318 `#b42318` is a raw color. Use a semantic color token. page.tsx:249:12 · #92400e `#92400e` is a raw color. Use a semantic color token. page.tsx:258:19 · 1px solid #eceef1 `1px solid #eceef1` is a raw color. Use a semantic color token. page.tsx:264:12 · #0f172a `#0f172a` is a raw color. Use a semantic color token. page.tsx:269:12 · #6b7280 `#6b7280` is a raw color. Use a semantic color token. page.tsx:287:12 · #6b7280 `#6b7280` is a raw color. Use a semantic color token. page.tsx:288:17 · #fafbfc `#fafbfc` is a raw color. Use a semantic color token. page.tsx:289:19 · 1px solid #eceef1 `1px solid #eceef1` is a raw color. Use a semantic color token. page.tsx:298:21 · 1px solid #f2f3f5 `1px solid #f2f3f5` is a raw color. Use a semantic color token. page.tsx:300:19 · #ffffff `#ffffff` is a raw color. Use a semantic color token. page.tsx:306:19 · #f9fafb `#f9fafb` is a raw color. Use a semantic color token. page.tsx:309:19 · #fffaeb `#fffaeb` is a raw color. Use a semantic color token. page.tsx:312:18 · inset 3px 0 0 0 #d97706 `inset 3px 0 0 0 #d97706` is a raw color. Use a semantic color token. page.tsx:316:12 · #4b5563 `#4b5563` is a raw color. Use a semantic color token. page.tsx:329:12 · #111827 `#111827` is a raw color. Use a semantic color token. Every run that broke raw-values/raw-color raw-values/raw-number 13 design-system page.tsx:135:17 · 700 `fontWeight: 700` skips the token scale. Use a fontWeight token. page.tsx:143:17 · 1.15 `lineHeight: 1.15` skips the token scale. Use a lineHeight token. page.tsx:144:17 · 700 `fontWeight: 700` skips the token scale. Use a fontWeight token. page.tsx:170:17 · 700 `fontWeight: 700` skips the token scale. Use a fontWeight token. page.tsx:178:17 · 650 `fontWeight: 650` skips the token scale. Use a fontWeight token. page.tsx:185:17 · 700 `fontWeight: 700` skips the token scale. Use a fontWeight token. page.tsx:205:17 · 1.1 `lineHeight: 1.1` skips the token scale. Use a lineHeight token. page.tsx:206:17 · 700 `fontWeight: 700` skips the token scale. Use a fontWeight token. page.tsx:230:17 · 700 `fontWeight: 700` skips the token scale. Use a fontWeight token. page.tsx:238:17 · 650 `fontWeight: 650` skips the token scale. Use a fontWeight token. page.tsx:263:17 · 700 `fontWeight: 700` skips the token scale. Use a fontWeight token. page.tsx:284:17 · 700 `fontWeight: 700` skips the token scale. Use a fontWeight token. page.tsx:328:17 · 500 `fontWeight: 500` skips the token scale. Use a fontWeight token. Every run that broke raw-values/raw-number typecheck/TS18048 5 quality a11y/color-contrast 2 quality page.tsx:1:1 · <p class="m_0 fs_12px fw_700 ls_0.12em tt_uppercase c_#6b7280">Current account statement</p> Elements must meet minimum color contrast ratio thresholds (3 elements, dark theme) page.tsx:1:1 · <p class="m_0 fs_12px fw_700 ls_0.12em tt_uppercase c_#6b7280">Current account statement</p> Elements must meet minimum color contrast ratio thresholds (3 elements, light theme) Every run that broke a11y/color-contrast typecheck/TS2769 1 quality 4 write_file 160 14,224 $0.0171
5 write_file 16,195 4,481 $0.0102