Runs / kyc-form 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 55 counted, under 9 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 list_files, read_file 827 66 $0.0003 1 read_file, read_file 1,165 253 $0.0007 2 write_file 688 5,949 $0.0074 3
Output cap The provider's default 3
4 import type { ChangeEvent, FormEvent } from "react";
5 import { css } from "styled-system/css";
6
7 import data from "./fixture.json";
8
9 const { customer, countries, documentTypes } = data;
10
11 type PepAnswer = "" | "yes" | "no";
12
13 type FormValues = {
14 dateOfBirth: string;
15 nationality: string;
16 countryOfResidence: string;
17 taxId: string;
18 documentType: string;
19 documentNumber: string;
20 pep: PepAnswer;
21 };
22
23 type FieldName = keyof FormValues;
24 type FormErrors = Partial<Record<FieldName, string>>;
25
26 const emptyValues: FormValues = {
27 dateOfBirth: "",
28 nationality: "",
29 countryOfResidence: "",
30 taxId: "",
31 documentType: "",
32 documentNumber: "",
33 pep: "",
34 };
35
36 const requiredMessages: Record<FieldName, string> = {
37 dateOfBirth: "Date of birth is required.",
38 nationality: "Nationality is required.",
39 countryOfResidence: "Country of residence is required.",
40 taxId: "Tax identification number is required.",
41 documentType: "Identity document type is required.",
42 documentNumber: "Document number is required.",
43 pep: "Please tell us whether you are a politically exposed person.",
44 };
45
46 const fieldNames = Object.keys(requiredMessages) as FieldName[];
47
48 function validate(values: FormValues): FormErrors {
49 const errors: FormErrors = {};
50
51 for (const name of fieldNames) {
52 const value = values[name];
53
54 if (value.trim() === "") {
55 errors[name] = requiredMessages[name];
56 }
57 }
58
59 return errors;
60 }
61
62 const pageStyles = css({
63 minHeight: "100vh",
64 bg: "gray.50",
65 paddingY: "10",
66 paddingX: "4",
67 });
68
69 const containerStyles = css({
70 width: "100%",
71 maxWidth: "42rem",
72 marginX: "auto",
73 display: "flex",
74 flexDirection: "column",
75 gap: "6",
76 });
77
78 const cardStyles = css({
79 bg: "white",
80 borderWidth: "1px",
81 borderStyle: "solid",
82 borderColor: "gray.200",
83 borderRadius: "lg",
84 padding: "6",
85 boxShadow: "sm",
86 });
87
88 const eyebrowStyles = css({
89 fontSize: "sm",
90 fontWeight: "semibold",
91 letterSpacing: "widest",
92 textTransform: "uppercase",
93 color: "gray.500",
94 marginBottom: "2",
95 });
96
97 const titleStyles = css({
98 fontSize: "2xl",
99 fontWeight: "bold",
100 color: "gray.900",
101 marginBottom: "2",
102 });
103
104 const ledeStyles = css({
105 fontSize: "sm",
106 color: "gray.600",
107 lineHeight: "1.6",
108 });
109
110 const summaryListStyles = css({
111 display: "grid",
112 gridTemplateColumns: { base: "1fr", sm: "1fr 1fr" },
113 gap: "4",
114 marginTop: "4",
115 });
116
117 const summaryTermStyles = css({
118 fontSize: "xs",
119 fontWeight: "medium",
120 textTransform: "uppercase",
121 letterSpacing: "wide",
122 color: "gray.500",
123 });
124
125 const summaryValueStyles = css({
126 fontSize: "md",
127 fontWeight: "medium",
128 color: "gray.900",
129 wordBreak: "break-word",
130 });
131
132 const lockedNoteStyles = css({
133 marginTop: "5",
134 fontSize: "xs",
135 color: "gray.500",
136 });
137
138 const formStyles = css({
139 display: "flex",
140 flexDirection: "column",
141 gap: "5",
142 });
143
144 const sectionTitleStyles = css({
145 fontSize: "lg",
146 fontWeight: "semibold",
147 color: "gray.900",
148 });
149
150 const gridStyles = css({
151 display: "grid",
152 gridTemplateColumns: { base: "1fr", sm: "1fr 1fr" },
153 columnGap: "4",
154 rowGap: "5",
155 });
156
157 const fieldStyles = css({
158 display: "flex",
159 flexDirection: "column",
160 gap: "1.5",
161 });
162
163 const labelStyles = css({
164 fontSize: "sm",
165 fontWeight: "medium",
166 color: "gray.800",
167 });
168
169 const controlStyles = css({
170 width: "100%",
171 borderRadius: "md",
172 borderWidth: "1px",
173 borderStyle: "solid",
174 borderColor: "gray.300",
175 bg: "white",
176 color: "gray.900",
177 fontSize: "md",
178 paddingX: "3",
179 paddingY: "2",
180 outline: "none",
181 _focusVisible: {
182 borderColor: "gray.900",
183 boxShadow: "0 0 0 3px token(colors.gray.200)",
184 },
185 _invalid: {
186 borderColor: "red.500",
187 },
188 });
189
190 const invalidControlStyles = css({
191 borderColor: "red.500",
192 });
193
194 const errorStyles = css({
195 fontSize: "sm",
196 color: "red.600",
197 });
198
199 const fieldsetStyles = css({
200 border: "none",
201 padding: "0",
202 margin: "0",
203 display: "flex",
204 flexDirection: "column",
205 gap: "1.5",
206 });
207
208 const radioRowStyles = css({
209 display: "flex",
210 flexWrap: "wrap",
211 gap: "5",
212 });
213
214 const radioOptionStyles = css({
215 display: "inline-flex",
216 alignItems: "center",
217 gap: "2",
218 fontSize: "md",
219 color: "gray.800",
220 cursor: "pointer",
221 });
222
223 const radioInputStyles = css({
224 width: "1rem",
225 height: "1rem",
226 accentColor: "gray.900",
227 cursor: "pointer",
228 });
229
230 const footerStyles = css({
231 display: "flex",
232 flexDirection: "column",
233 gap: "3",
234 alignItems: "flex-start",
235 });
236
237 const submitStyles = css({
238 borderRadius: "md",
239 border: "none",
240 bg: "gray.900",
241 color: "white",
242 fontSize: "md",
243 fontWeight: "semibold",
244 paddingX: "6",
245 paddingY: "3",
246 cursor: "pointer",
247 _hover: { bg: "gray.800" },
248 _focusVisible: {
249 outline: "2px solid token(colors.gray.900)",
250 outlineOffset: "2px",
251 },
252 });
253
254 const successStyles = css({
255 display: "flex",
256 flexDirection: "column",
257 gap: "3",
258 });
259
260 const successTitleStyles = css({
261 fontSize: "lg",
262 fontWeight: "semibold",
263 color: "gray.900",
264 });
265
266 const successBodyStyles = css({
267 fontSize: "sm",
268 color: "gray.600",
269 lineHeight: "1.6",
270 });
271
272 export default function IdentityVerificationPage() {
273 const [values, setValues] = useState<FormValues>(emptyValues);
274 const [errors, setErrors] = useState<FormErrors>({});
275 const [submitted, setSubmitted] = useState(false);
276 const uid = useId();
277
278 const fieldId = (name: FieldName) => `${uid}-${name}`;
279 const errorId = (name: FieldName) => `${uid}-${name}-error`;
280
281 const handleChange =
282 (name: FieldName) =>
283 (event: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
284 const { value } = event.target;
285
286 setValues((previous) => ({ ...previous, [name]: value }));
287
288 setErrors((previous) => {
289 if (!(name in previous)) return previous;
290
291 if (value.trim() !== "") {
292 const next = { ...previous };
293 delete next[name];
294 return next;
295 }
296
297 return previous;
298 });
299 };
300
301 const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
302 event.preventDefault();
303
304 const nextErrors = validate(values);
305 setErrors(nextErrors);
306
307 const firstInvalid = fieldNames.find((name) => nextErrors[name] !== undefined);
308
309 if (firstInvalid) {
310 const control = document.getElementById(fieldId(firstInvalid));
311 control?.focus();
312 return;
313 }
314
315 setSubmitted(true);
316 };
317
318 const describedBy = (name: FieldName) =>
319 errors[name] ? errorId(name) : undefined;
320
321 const controlClassName = (name: FieldName) =>
322 errors[name]
323 ? `${controlStyles} ${invalidControlStyles}`
324 : controlStyles;
325
326 const renderError = (name: FieldName) =>
327 errors[name] ? (
328 <p id={errorId(name)} className={errorStyles} role="alert">
329 {errors[name]}
330 </p>
331 ) : null;
332
333 return (
334 <main className={pageStyles}>
335 <div className={containerStyles}>
336 <section className={cardStyles} aria-labelledby={`${uid}-heading`}>
337 <p className={eyebrowStyles}>Account opening</p>
338 <h1 id={`${uid}-heading`} className={titleStyles}>
339 Identity verification
340 </h1>
341 <p className={ledeStyles}>
342 We need to confirm who you are before we can open your account.
343 This usually takes a few minutes.
344 </p>
345
346 <dl className={summaryListStyles}>
347 <div>
348 <dt className={summaryTermStyles}>Full name</dt>
349 <dd className={summaryValueStyles}>
350 {`${customer.firstName} ${customer.lastName}`}
351 </dd>
352 </div>
353 <div>
354 <dt className={summaryTermStyles}>Email address</dt>
355 <dd className={summaryValueStyles}>{customer.email}</dd>
356 </div>
357 </dl>
358 <p className={lockedNoteStyles}>
359 These details are from your application and cannot be changed here.
360 </p>
361 </section>
362
363 <section className={cardStyles} aria-labelledby={`${uid}-form-heading`}>
364 <h2 id={`${uid}-form-heading`} className={sectionTitleStyles}>
365 {submitted ? "Details under review" : "Tell us about yourself"}
366 </h2>
367
368 {submitted ? (
369 <div className={successStyles}>
370 <p className={successTitleStyles}>Your details are being reviewed</p>
371 <p className={successBodyStyles}>
372 Thanks{` ${customer.firstName}`}. Our identity team is checking the
373 information you provided. We will email you at {customer.email} as
374 soon as the review is complete.
375 </p>
376 </div>
377 ) : (
378 <form className={formStyles} onSubmit={handleSubmit} noValidate>
379 <div className={gridStyles}>
380 <div className={fieldStyles}>
381 <label className={labelStyles} htmlFor={fieldId("dateOfBirth")}>
382 Date of birth
383 </label>
384 <input
385 id={fieldId("dateOfBirth")}
386 className={controlClassName("dateOfBirth")}
387 type="date"
388 name="dateOfBirth"
389 value={values.dateOfBirth}
390 onChange={handleChange("dateOfBirth")}
391 aria-invalid={errors.dateOfBirth ? true : undefined}
392 aria-describedby={describedBy("dateOfBirth")}
393 />
394 {renderError("dateOfBirth")}
395 </div>
396
397 <div className={fieldStyles}>
398 <label className={labelStyles} htmlFor={fieldId("nationality")}>
399 Nationality
400 </label>
401 <select
402 id={fieldId("nationality")}
403 className={controlClassName("nationality")}
404 name="nationality"
405 value={values.nationality}
406 onChange={handleChange("nationality")}
407 aria-invalid={errors.nationality ? true : undefined}
408 aria-describedby={describedBy("nationality")}
409 >
410 <option value="">Select a country</option>
411 {countries.map((country) => (
412 <option key={country.code} value={country.name}>
413 {country.name}
414 </option>
415 ))}
416 </select>
417 {renderError("nationality")}
418 </div>
419
420 <div className={fieldStyles}>
421 <label
422 className={labelStyles}
423 htmlFor={fieldId("countryOfResidence")}
424 >
425 Country of residence
426 </label>
427 <select
428 id={fieldId("countryOfResidence")}
429 className={controlClassName("countryOfResidence")}
430 name="countryOfResidence"
431 value={values.countryOfResidence}
432 onChange={handleChange("countryOfResidence")}
433 aria-invalid={errors.countryOfResidence ? true : undefined}
434 aria-describedby={describedBy("countryOfResidence")}
435 >
436 <option value="">Select a country</option>
437 {countries.map((country) => (
438 <option key={country.code} value={country.name}>
439 {country.name}
440 </option>
441 ))}
442 </select>
443 {renderError("countryOfResidence")}
444 </div>
445
446 <div className={fieldStyles}>
447 <label className={labelStyles} htmlFor={fieldId("taxId")}>
448 Tax identification number
449 </label>
450 <input
451 id={fieldId("taxId")}
452 className={controlClassName("taxId")}
453 type="text"
454 name="taxId"
455 autoComplete="off"
456 value={values.taxId}
457 onChange={handleChange("taxId")}
458 aria-invalid={errors.taxId ? true : undefined}
459 aria-describedby={describedBy("taxId")}
460 />
461 {renderError("taxId")}
462 </div>
463
464 <div className={fieldStyles}>
465 <label className={labelStyles} htmlFor={fieldId("documentType")}>
466 Type of identity document
467 </label>
468 <select
469 id={fieldId("documentType")}
470 className={controlClassName("documentType")}
471 name="documentType"
472 value={values.documentType}
473 onChange={handleChange("documentType")}
474 aria-invalid={errors.documentType ? true : undefined}
475 aria-describedby={describedBy("documentType")}
476 >
477 <option value="">Select a document type</option>
478 {documentTypes.map((document) => (
479 <option key={document.value} value={document.value}>
480 {document.label}
481 </option>
482 ))}
483 </select>
484 {renderError("documentType")}
485 </div>
486
487 <div className={fieldStyles}>
488 <label
489 className={labelStyles}
490 htmlFor={fieldId("documentNumber")}
491 >
492 Document number
493 </label>
494 <input
495 id={fieldId("documentNumber")}
496 className={controlClassName("documentNumber")}
497 type="text"
498 name="documentNumber"
499 autoComplete="off"
500 value={values.documentNumber}
501 onChange={handleChange("documentNumber")}
502 aria-invalid={errors.documentNumber ? true : undefined}
503 aria-describedby={describedBy("documentNumber")}
504 />
505 {renderError("documentNumber")}
506 </div>
507 </div>
508
509 <fieldset
510 className={fieldsetStyles}
511 aria-invalid={errors.pep ? true : undefined}
512 aria-describedby={describedBy("pep")}
513 >
514 <legend className={labelStyles}>Politically exposed person</legend>
515 <div className={radioRowStyles}>
516 <label className={radioOptionStyles} htmlFor={fieldId("pep")}>
517 <input
518 id={fieldId("pep")}
519 className={radioInputStyles}
520 type="radio"
521 name="pep"
522 value="yes"
523 checked={values.pep === "yes"}
524 onChange={handleChange("pep")}
525 />
526 Yes
527 </label>
528 <label
529 className={radioOptionStyles}
530 htmlFor={`${fieldId("pep")}-no`}
531 >
532 <input
533 id={`${fieldId("pep")}-no`}
534 className={radioInputStyles}
535 type="radio"
536 name="pep"
537 value="no"
538 checked={values.pep === "no"}
539 onChange={handleChange("pep")}
540 />
541 No
542 </label>
543 </div>
544 {renderError("pep")}
545 </fieldset>
546
547 <div className={footerStyles}>
548 <button type="submit" className={submitStyles}>
549 Submit for verification
550 </button>
551 </div>
552 </form>
553 )}
554 </section>
555 </div>
556 </main>
557 );
558 }
559
`gray.900` is a palette shade: it ignores the theme. Use a semantic color token.
page.tsx:135:10 · gray.500 `gray.500` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:147:10 · gray.900 `gray.900` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:166:10 · gray.800 `gray.800` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:174:16 · gray.300 `gray.300` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:176:10 · gray.900 `gray.900` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:182:18 · gray.900 `gray.900` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:186:18 · red.500 `red.500` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:191:16 · red.500 `red.500` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:196:10 · red.600 `red.600` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:219:10 · gray.800 `gray.800` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:240:7 · gray.900 `gray.900` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:247:17 · gray.800 `gray.800` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:263:10 · gray.900 `gray.900` is a palette shade: it ignores the theme. Use a semantic color token. page.tsx:268:10 · gray.600 `gray.600` is a palette shade: it ignores the theme. Use a semantic color token. components/native-element 9 design-system page.tsx:384:19 · <input> `<input>` has a design-system equivalent: use `Input` from `@vektos/ds`. page.tsx:401:19 · <select> `<select>` has a design-system equivalent: use `Select` from `@vektos/ds`. page.tsx:427:19 · <select> `<select>` has a design-system equivalent: use `Select` from `@vektos/ds`. page.tsx:450:19 · <input> `<input>` has a design-system equivalent: use `Input` from `@vektos/ds`. page.tsx:468:19 · <select> `<select>` has a design-system equivalent: use `Select` from `@vektos/ds`. page.tsx:494:19 · <input> `<input>` has a design-system equivalent: use `Input` from `@vektos/ds`. page.tsx:517:21 · <input> `<input>` has a design-system equivalent: use `Input` from `@vektos/ds`. page.tsx:532:21 · <input> `<input>` has a design-system equivalent: use `Input` from `@vektos/ds`. page.tsx:548:17 · <button> `<button>` has a design-system equivalent: use `Button` from `@vektos/ds`. Every run that broke components/native-element raw-values/raw-length 9 design-system typecheck/TS2769 5 quality lint/@pandacss(no-invalid-token-paths) 2 quality