Runs / loan-contract 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 19 counted, under 4 rules. “Not counted” marks a compiler error that restates a design-system violation, “Ignored” one the agent silenced with a comment.
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 get_guidelines, list_components, list_files 975 112 $0.0031 1 read_file, list_tokens, get_component, get_component, get_component 1,967 204 $0.0061 2 write_file 4,347 12,459 $0.1338
Output cap The provider's default 4 CardBody,
5 CardDescription,
6 CardHeader,
7 CardTitle,
8 Table,
9 TableBody,
10 TableCell,
11 TableHead,
12 TableHeader,
13 TableRow,
14 } from "@vektos/ds";
15 import { css } from "styled-system/css";
16 import data from "./fixture.json";
17
18 const currencyFormatter = new Intl.NumberFormat("en-IE", {
19 style: "currency",
20 currency: data.contract.currency,
21 minimumFractionDigits: 2,
22 maximumFractionDigits: 2,
23 });
24
25 const percentFormatter = new Intl.NumberFormat("en-IE", {
26 style: "percent",
27 minimumFractionDigits: 2,
28 maximumFractionDigits: 2,
29 });
30
31 const dateFormatter = new Intl.DateTimeFormat("en-GB", {
32 day: "numeric",
33 month: "short",
34 year: "numeric",
35 timeZone: "UTC",
36 });
37
38 function formatCurrency(value: number) {
39 return currencyFormatter.format(value);
40 }
41
42 function formatPercent(value: number) {
43 return percentFormatter.format(value);
44 }
45
46 function formatDate(value: string) {
47 return dateFormatter.format(new Date(`${value}T00:00:00Z`));
48 }
49
50 function statusLabel(status: string) {
51 return status.charAt(0).toUpperCase() + status.slice(1);
52 }
53
54 function InstalmentStatus({ status }: { status: string }) {
55 if (status === "paid") {
56 return <Badge tone="success">Paid</Badge>;
57 }
58
59 if (status === "due") {
60 return <Badge tone="warning">Due</Badge>;
61 }
62
63 return <Badge tone="neutral">{statusLabel(status)}</Badge>;
64 }
65
66 const termGrid = css({
67 display: "grid",
68 gridTemplateColumns: {
69 base: "1fr",
70 sm: "repeat(2, minmax(0, 1fr))",
71 xl: "repeat(3, minmax(0, 1fr))",
72 },
73 gap: "4",
74 });
75
76 const termItem = css({
77 bg: "bg.subtle",
78 rounded: "md",
79 p: "4",
80 });
81
82 const termLabel = css({
83 color: "fg.muted",
84 fontSize: "xs",
85 lineHeight: "normal",
86 });
87
88 const termValue = css({
89 m: "0",
90 mt: "1",
91 color: "fg.default",
92 fontSize: "md",
93 fontWeight: "semibold",
94 lineHeight: "tight",
95 });
96
97 const documentItem = css({
98 display: "flex",
99 alignItems: "center",
100 justifyContent: "space-between",
101 flexWrap: "wrap",
102 gap: "4",
103 py: "4",
104 _first: { pt: "0" },
105 _last: { pb: "0" },
106 "& + li": {
107 borderTopWidth: "thin",
108 borderTopStyle: "solid",
109 borderTopColor: "border.default",
110 },
111 });
112
113 export default function PersonalLoanContractPage() {
114 const { contract } = data;
115 const nextPayment =
116 data.schedule.find((instalment) => instalment.status === "due") ??
117 data.schedule.find((instalment) => instalment.status === "upcoming");
118
119 return (
120 <div className={`vektos ${css({ bg: "bg.canvas", color: "fg.default" })}`}>
121 <main
122 className={css({
123 width: "full",
124 mx: "auto",
125 px: { base: "4", md: "8" },
126 py: { base: "6", md: "8" },
127 display: "grid",
128 gap: "8",
129 })}
130 >
131 <header
132 className={css({
133 display: "flex",
134 alignItems: { base: "flex-start", md: "center" },
135 justifyContent: "space-between",
136 flexWrap: "wrap",
137 gap: "6",
138 })}
139 >
140 <div className={css({ display: "grid", gap: "2" })}>
141 <p
142 className={css({
143 color: "accent.default",
144 fontSize: "xs",
145 fontWeight: "semibold",
146 textTransform: "uppercase",
147 })}
148 >
149 Loan contract
150 </p>
151 <div
152 className={css({
153 display: "flex",
154 alignItems: "center",
155 flexWrap: "wrap",
156 gap: "3",
157 })}
158 >
159 <h1
160 className={css({
161 fontSize: "lg",
162 fontWeight: "semibold",
163 lineHeight: "tight",
164 })}
165 >
166 {contract.product}
167 </h1>
168 <Badge tone={contract.status === "active" ? "success" : "neutral"}>
169 {statusLabel(contract.status)}
170 </Badge>
171 </div>
172 <p className={css({ color: "fg.muted", fontSize: "sm" })}>
173 Borrower{" "}
174 <span className={css({ color: "fg.default", fontWeight: "medium" })}>
175 {contract.borrower}
176 </span>
177 </p>
178 </div>
179
180 <div className={css({ textAlign: { base: "left", md: "right" } })}>
181 <p className={termLabel}>Contract number</p>
182 <p
183 className={css({
184 mt: "1",
185 fontSize: "md",
186 fontWeight: "semibold",
187 lineHeight: "tight",
188 })}
189 >
190 {contract.number}
191 </p>
192 </div>
193 </header>
194
195 <section
196 aria-label="Loan terms and current balance"
197 className={css({
198 display: "flex",
199 flexDirection: { base: "column", lg: "row" },
200 alignItems: "stretch",
201 gap: "6",
202 })}
203 >
204 <Card className={css({ flex: "1" })}>
205 <CardHeader>
206 <CardTitle asChild>
207 <h2>Key terms</h2>
208 </CardTitle>
209 <CardDescription>
210 The original borrowing conditions for this contract.
211 </CardDescription>
212 </CardHeader>
213 <CardBody>
214 <dl className={termGrid}>
215 <div className={termItem}>
216 <dt className={termLabel}>Amount borrowed</dt>
217 <dd className={termValue}>{formatCurrency(contract.principal)}</dd>
218 </div>
219 <div className={termItem}>
220 <dt className={termLabel}>Nominal interest rate</dt>
221 <dd className={termValue}>{formatPercent(contract.nominalRate)}</dd>
222 </div>
223 <div className={termItem}>
224 <dt className={termLabel}>APR</dt>
225 <dd className={termValue}>{formatPercent(contract.apr)}</dd>
226 </div>
227 <div className={termItem}>
228 <dt className={termLabel}>Term</dt>
229 <dd className={termValue}>{contract.termMonths} months</dd>
230 </div>
231 <div className={termItem}>
232 <dt className={termLabel}>Monthly payment</dt>
233 <dd className={termValue}>{formatCurrency(contract.monthlyPayment)}</dd>
234 </div>
235 <div className={termItem}>
236 <dt className={termLabel}>Start date</dt>
237 <dd className={termValue}>
238 <time dateTime={contract.startDate}>{formatDate(contract.startDate)}</time>
239 </dd>
240 </div>
241 </dl>
242 </CardBody>
243 </Card>
244
245 <Card
246 className={css({
247 width: { base: "full", lg: "60" },
248 flexShrink: 0,
249 display: "flex",
250 flexDirection: "column",
251 })}
252 >
253 <CardHeader>
254 <CardTitle asChild>
255 <h2>Outstanding balance</h2>
256 </CardTitle>
257 <CardDescription>Amount still owed on this contract.</CardDescription>
258 </CardHeader>
259 <CardBody
260 className={css({
261 flex: "1",
262 display: "flex",
263 flexDirection: "column",
264 justifyContent: "space-between",
265 gap: "6",
266 })}
267 >
268 <p
269 className={css({
270 fontSize: "lg",
271 fontWeight: "semibold",
272 lineHeight: "tight",
273 })}
274 >
275 {formatCurrency(contract.outstanding)}
276 </p>
277
278 {nextPayment ? (
279 <div
280 className={css({
281 borderTopWidth: "thin",
282 borderTopStyle: "solid",
283 borderTopColor: "border.default",
284 pt: "4",
285 })}
286 >
287 <p className={termLabel}>Next payment</p>
288 <p
289 className={css({
290 mt: "1",
291 fontSize: "md",
292 fontWeight: "semibold",
293 })}
294 >
295 {formatCurrency(nextPayment.payment)}
296 </p>
297 <p className={css({ mt: "1", color: "fg.muted", fontSize: "xs" })}>
298 Due <time dateTime={nextPayment.dueDate}>{formatDate(nextPayment.dueDate)}</time>
299 </p>
300 </div>
301 ) : null}
302 </CardBody>
303 </Card>
304 </section>
305
306 <Card>
307 <CardHeader>
308 <CardTitle asChild>
309 <h2>Repayment schedule</h2>
310 </CardTitle>
311 <CardDescription>
312 {data.schedule.length} instalments shown, with the payment split and balance left after each one.
313 </CardDescription>
314 </CardHeader>
315 <div
316 role="region"
317 aria-label="Scrollable repayment schedule"
318 tabIndex={0}
319 className={css({ overflowX: "auto", mt: "4" })}
320 >
321 <Table
322 aria-label={`Repayment schedule for contract ${contract.number}`}
323 className={css({ width: "full", whiteSpace: "nowrap" })}
324 >
325 <TableHead>
326 <TableRow>
327 <TableHeader numeric>No.</TableHeader>
328 <TableHeader>Due date</TableHeader>
329 <TableHeader numeric>Payment</TableHeader>
330 <TableHeader numeric>Principal</TableHeader>
331 <TableHeader numeric>Interest</TableHeader>
332 <TableHeader numeric>Balance left</TableHeader>
333 <TableHeader>Status</TableHeader>
334 </TableRow>
335 </TableHead>
336 <TableBody>
337 {data.schedule.map((instalment) => (
338 <TableRow key={instalment.number}>
339 <TableCell numeric>{instalment.number}</TableCell>
340 <TableCell>
341 <time dateTime={instalment.dueDate}>{formatDate(instalment.dueDate)}</time>
342 </TableCell>
343 <TableCell numeric className={css({ fontWeight: "medium" })}>
344 {formatCurrency(instalment.payment)}
345 </TableCell>
346 <TableCell numeric>{formatCurrency(instalment.principal)}</TableCell>
347 <TableCell numeric>{formatCurrency(instalment.interest)}</TableCell>
348 <TableCell numeric className={css({ fontWeight: "medium" })}>
349 {formatCurrency(instalment.remaining)}
350 </TableCell>
351 <TableCell>
352 <InstalmentStatus status={instalment.status} />
353 </TableCell>
354 </TableRow>
355 ))}
356 </TableBody>
357 </Table>
358 </div>
359 </Card>
360
361 <Card>
362 <CardHeader>
363 <CardTitle asChild>
364 <h2>Signed documents</h2>
365 </CardTitle>
366 <CardDescription>
367 {data.documents.length} documents signed for this loan contract.
368 </CardDescription>
369 </CardHeader>
370 <CardBody>
371 <ul aria-label="Signed documents">
372 {data.documents.map((document) => (
373 <li key={document.name} className={documentItem}>
374 <div className={css({ display: "flex", alignItems: "center", gap: "3" })}>
375 <span
376 aria-hidden="true"
377 className={css({
378 width: "8",
379 height: "8",
380 flexShrink: 0,
381 display: "flex",
382 alignItems: "center",
383 justifyContent: "center",
384 rounded: "md",
385 bg: "bg.subtle",
386 color: "fg.muted",
387 })}
388 >
389 <svg
390 viewBox="0 0 24 24"
391 fill="none"
392 stroke="currentColor"
393 strokeWidth="1.5"
394 strokeLinecap="round"
395 strokeLinejoin="round"
396 className={css({ width: "4", height: "4" })}
397 >
398 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" />
399 <path d="M14 2v6h6M8 13h8M8 17h5" />
400 </svg>
401 </span>
402 <span className={css({ fontSize: "sm", fontWeight: "medium" })}>
403 {document.name}
404 </span>
405 </div>
406 <p className={css({ color: "fg.muted", fontSize: "sm", whiteSpace: "nowrap" })}>
407 Signed on{" "}
408 <time dateTime={document.signedAt} className={css({ color: "fg.default" })}>
409 {formatDate(document.signedAt)}
410 </time>
411 </p>
412 </li>
413 ))}
414 </ul>
415 </CardBody>
416 </Card>
417 </main>
418 </div>
419 );
420 }
421
Use the logical property "marginBlockStart" instead of the physical "marginTop".
page.tsx:297:39 Use the logical property "marginBlockStart" instead of the physical "marginTop". page.tsx:319:49 Use the logical property "marginBlockStart" instead of the physical "marginTop". typecheck/TS2769 7 quality lint/jsx-a11y(no-noninteractive-tabindex) 1 quality lint/jsx-a11y(prefer-tag-over-role) 1 quality