Answer sheet
const ROWS = [
{ q: '4', filled: 'B' },
{ q: '5', filled: 'D' },
];
const LETTERS = ['A', 'B', 'C', 'D'];
const SCRIBBLE = 'M4 5L31 3L4 8L32 6L3 11L33 9L3 14L33 12L4 17L32 15L5 20L30 18L8 22L28 21';
const css = `
.cb-answer-sheet { --paper: light-dark(#ebedf0, #e5e6e9); --print: light-dark(#373a40, #121417); --soft: color-mix(in oklab, var(--print) 74%, var(--paper)); --graphite: color-mix(in oklab, var(--print) 86%, var(--paper)); --wood: light-dark(#6d7179, #7d8087); flex: none; position: relative; display: block; width: 244px; height: 222px; box-sizing: border-box; padding: 12px 14px 10px 30px; background: var(--paper); color: var(--print); font-family: 'Courier New', Courier, monospace; clip-path: polygon(0 0, calc(100% - 24px) 0, 100% 24px, 100% 100%, 0 100%); cursor: pointer; -webkit-user-select: none; user-select: none; }
.cb-answer-sheet__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.cb-answer-sheet__marks { position: absolute; left: 9px; top: 12px; bottom: 12px; width: 11px; background: repeating-linear-gradient(to bottom, var(--print) 0 5px, transparent 5px 14px); }
.cb-answer-sheet__head { display: block; padding-bottom: 6px; border-bottom: 2px solid var(--print); font: 700 10px/1.2 system-ui, sans-serif; letter-spacing: 0.1em; text-transform: uppercase; }
.cb-answer-sheet__note { display: block; margin-top: 3px; font: 500 8.5px/1.2 system-ui, sans-serif; letter-spacing: 0.02em; }
.cb-answer-sheet__row { display: flex; align-items: center; gap: 7px; height: 24px; font-size: 11px; font-weight: 700; }
.cb-answer-sheet__row b { width: 16px; font-weight: 700; }
.cb-answer-sheet__opt { display: grid; place-items: center; width: 24px; height: 14px; border: 1px solid var(--print); border-radius: 50%; font: 700 8px/1 system-ui, sans-serif; }
.cb-answer-sheet__opt--in { background: var(--graphite); }
.cb-answer-sheet__real { display: flex; align-items: center; gap: 9px; margin-top: 7px; padding: 8px 0; border-top: 1px solid var(--print); border-bottom: 1px solid var(--print); }
.cb-answer-sheet__real b { font-size: 11px; font-weight: 700; }
.cb-answer-sheet__oval { position: relative; flex: none; display: block; box-sizing: border-box; width: 44px; height: 26px; border: 1.5px solid var(--print); border-radius: 50%; }
.cb-answer-sheet__input:focus-visible ~ .cb-answer-sheet__real .cb-answer-sheet__oval { outline: 2px solid var(--print); outline-offset: 2px; }
.cb-answer-sheet__mark { position: absolute; left: 3px; top: 0; width: 38px; height: 26px; overflow: visible; }
.cb-answer-sheet__lead { fill: none; stroke: var(--graphite); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 0.2s; }
.cb-answer-sheet__input:checked ~ .cb-answer-sheet__real .cb-answer-sheet__lead { stroke-dashoffset: 0; transition: stroke-dashoffset 0.72s linear 0.14s; }
.cb-answer-sheet__pencil { position: absolute; left: 0; top: 0; width: 86px; height: 14px; offset-path: path('M4 5L31 3L4 8L32 6L3 11L33 9L3 14L33 12L4 17L32 15L5 20L30 18L8 22L28 21'); offset-rotate: 0deg; offset-anchor: 0 100%; offset-distance: 0%; opacity: 0; transform: rotate(-38deg); transform-origin: 0 100%; pointer-events: none; }
.cb-answer-sheet__input:checked ~ .cb-answer-sheet__real .cb-answer-sheet__pencil { animation: cb-answer-sheet-write 0.92s linear; }
@keyframes cb-answer-sheet-write {
0% { opacity: 0; offset-distance: 0%; transform: translate(14px, -18px) rotate(-38deg); }
14% { opacity: 1; offset-distance: 0%; transform: rotate(-38deg); }
90% { opacity: 1; offset-distance: 100%; transform: rotate(-38deg); }
100% { opacity: 0; offset-distance: 100%; transform: translate(18px, -14px) rotate(-38deg); }
}
.cb-answer-sheet__wood { fill: var(--wood); }
.cb-answer-sheet__tip { fill: var(--paper); stroke: var(--print); stroke-width: 0.7; }
.cb-answer-sheet__lead-tip { fill: var(--print); }
.cb-answer-sheet__band { fill: var(--print); }
.cb-answer-sheet__text { min-width: 0; white-space: nowrap; font: 700 13px/1.15 Georgia, 'Times New Roman', serif; }
.cb-answer-sheet__foot { position: absolute; left: 30px; right: 14px; bottom: 9px; display: flex; justify-content: space-between; font: 700 8px/1 'Courier New', Courier, monospace; letter-spacing: 0.06em; }
@media (prefers-reduced-motion: reduce) {
.cb-answer-sheet__lead, .cb-answer-sheet__input:checked ~ .cb-answer-sheet__real .cb-answer-sheet__lead { transition: none; }
.cb-answer-sheet__input:checked ~ .cb-answer-sheet__real .cb-answer-sheet__pencil { animation: none; }
}
`;
export function AnswerSheetCheckbox() {
return (
<>
<style>
{css}
</style>
<label className="cb-answer-sheet">
<span className="cb-answer-sheet__marks" aria-hidden="true" />
<span className="cb-answer-sheet__head" aria-hidden="true">
Answer sheet
<span className="cb-answer-sheet__note">Use a soft pencil. Fill the oval completely.</span>
</span>
<span aria-hidden="true">
{ROWS.map((row) => (
<span key={row.q} className="cb-answer-sheet__row">
<b>{row.q}.</b>
{LETTERS.map((l) => (
<span key={l} className={`cb-answer-sheet__opt${row.filled === l ? ' cb-answer-sheet__opt--in' : ''}`}>
{row.filled === l ? '' : l}
</span>
))}
</span>
))}
</span>
<input className="cb-answer-sheet__input" type="checkbox" />
<span className="cb-answer-sheet__real">
<span className="cb-answer-sheet__oval" aria-hidden="true">
<svg className="cb-answer-sheet__mark" viewBox="0 0 38 26">
<path className="cb-answer-sheet__lead" pathLength={1} d={SCRIBBLE} />
</svg>
<svg className="cb-answer-sheet__pencil" viewBox="0 0 86 14">
<path className="cb-answer-sheet__lead-tip" d="M0 12L3.5 9.5V13.5Z" />
<path className="cb-answer-sheet__tip" d="M3 9.2L12 6V14L3 13.6Z" />
<rect className="cb-answer-sheet__wood" x="12" y="6" width="58" height="8" />
<rect className="cb-answer-sheet__band" x="70" y="6" width="5" height="8" />
<rect className="cb-answer-sheet__tip" x="75" y="6" width="9" height="8" rx="2" />
</svg>
</span>
<span className="cb-answer-sheet__text">6. I agree to the terms</span>
</span>
<span aria-hidden="true">
<span className="cb-answer-sheet__row">
<b>7.</b>
{LETTERS.map((l) => (
<span key={l} className="cb-answer-sheet__opt">
{l}
</span>
))}
</span>
</span>
<span className="cb-answer-sheet__foot" aria-hidden="true">
<span>FORM 22-B</span>
<span>PAGE 3 OF 8</span>
</span>
</label>
</>
);
}Markup where the component goes, CSS in your stylesheet. No JavaScript.
<label class="cb-answer-sheet">
<span class="cb-answer-sheet__marks" aria-hidden="true"></span>
<span class="cb-answer-sheet__head" aria-hidden="true">
Answer sheet
<span class="cb-answer-sheet__note">Use a soft pencil. Fill the oval completely.</span>
</span>
<span aria-hidden="true">
<span class="cb-answer-sheet__row">
<b>4.</b>
<span class="cb-answer-sheet__opt">A</span>
<span class="cb-answer-sheet__opt cb-answer-sheet__opt--in"></span>
<span class="cb-answer-sheet__opt">C</span>
<span class="cb-answer-sheet__opt">D</span>
</span>
<span class="cb-answer-sheet__row">
<b>5.</b>
<span class="cb-answer-sheet__opt">A</span>
<span class="cb-answer-sheet__opt">B</span>
<span class="cb-answer-sheet__opt">C</span>
<span class="cb-answer-sheet__opt cb-answer-sheet__opt--in"></span>
</span>
</span>
<input class="cb-answer-sheet__input" type="checkbox"/>
<span class="cb-answer-sheet__real">
<span class="cb-answer-sheet__oval" aria-hidden="true">
<svg class="cb-answer-sheet__mark" viewBox="0 0 38 26">
<path class="cb-answer-sheet__lead" pathLength="1" d="M4 5L31 3L4 8L32 6L3 11L33 9L3 14L33 12L4 17L32 15L5 20L30 18L8 22L28 21"></path>
</svg>
<svg class="cb-answer-sheet__pencil" viewBox="0 0 86 14">
<path class="cb-answer-sheet__lead-tip" d="M0 12L3.5 9.5V13.5Z"></path>
<path class="cb-answer-sheet__tip" d="M3 9.2L12 6V14L3 13.6Z"></path>
<rect class="cb-answer-sheet__wood" x="12" y="6" width="58" height="8"></rect>
<rect class="cb-answer-sheet__band" x="70" y="6" width="5" height="8"></rect>
<rect class="cb-answer-sheet__tip" x="75" y="6" width="9" height="8" rx="2"></rect>
</svg>
</span>
<span class="cb-answer-sheet__text">6. I agree to the terms</span>
</span>
<span aria-hidden="true">
<span class="cb-answer-sheet__row">
<b>7.</b>
<span class="cb-answer-sheet__opt">A</span>
<span class="cb-answer-sheet__opt">B</span>
<span class="cb-answer-sheet__opt">C</span>
<span class="cb-answer-sheet__opt">D</span>
</span>
</span>
<span class="cb-answer-sheet__foot" aria-hidden="true">
<span>FORM 22-B</span>
<span>PAGE 3 OF 8</span>
</span>
</label>.cb-answer-sheet { --paper: light-dark(#ebedf0, #e5e6e9); --print: light-dark(#373a40, #121417); --soft: color-mix(in oklab, var(--print) 74%, var(--paper)); --graphite: color-mix(in oklab, var(--print) 86%, var(--paper)); --wood: light-dark(#6d7179, #7d8087); flex: none; position: relative; display: block; width: 244px; height: 222px; box-sizing: border-box; padding: 12px 14px 10px 30px; background: var(--paper); color: var(--print); font-family: 'Courier New', Courier, monospace; clip-path: polygon(0 0, calc(100% - 24px) 0, 100% 24px, 100% 100%, 0 100%); cursor: pointer; -webkit-user-select: none; user-select: none; }
.cb-answer-sheet__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.cb-answer-sheet__marks { position: absolute; left: 9px; top: 12px; bottom: 12px; width: 11px; background: repeating-linear-gradient(to bottom, var(--print) 0 5px, transparent 5px 14px); }
.cb-answer-sheet__head { display: block; padding-bottom: 6px; border-bottom: 2px solid var(--print); font: 700 10px/1.2 system-ui, sans-serif; letter-spacing: 0.1em; text-transform: uppercase; }
.cb-answer-sheet__note { display: block; margin-top: 3px; font: 500 8.5px/1.2 system-ui, sans-serif; letter-spacing: 0.02em; }
.cb-answer-sheet__row { display: flex; align-items: center; gap: 7px; height: 24px; font-size: 11px; font-weight: 700; }
.cb-answer-sheet__row b { width: 16px; font-weight: 700; }
.cb-answer-sheet__opt { display: grid; place-items: center; width: 24px; height: 14px; border: 1px solid var(--print); border-radius: 50%; font: 700 8px/1 system-ui, sans-serif; }
.cb-answer-sheet__opt--in { background: var(--graphite); }
.cb-answer-sheet__real { display: flex; align-items: center; gap: 9px; margin-top: 7px; padding: 8px 0; border-top: 1px solid var(--print); border-bottom: 1px solid var(--print); }
.cb-answer-sheet__real b { font-size: 11px; font-weight: 700; }
.cb-answer-sheet__oval { position: relative; flex: none; display: block; box-sizing: border-box; width: 44px; height: 26px; border: 1.5px solid var(--print); border-radius: 50%; }
.cb-answer-sheet__input:focus-visible ~ .cb-answer-sheet__real .cb-answer-sheet__oval { outline: 2px solid var(--print); outline-offset: 2px; }
.cb-answer-sheet__mark { position: absolute; left: 3px; top: 0; width: 38px; height: 26px; overflow: visible; }
.cb-answer-sheet__lead { fill: none; stroke: var(--graphite); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 0.2s; }
.cb-answer-sheet__input:checked ~ .cb-answer-sheet__real .cb-answer-sheet__lead { stroke-dashoffset: 0; transition: stroke-dashoffset 0.72s linear 0.14s; }
.cb-answer-sheet__pencil { position: absolute; left: 0; top: 0; width: 86px; height: 14px; offset-path: path('M4 5L31 3L4 8L32 6L3 11L33 9L3 14L33 12L4 17L32 15L5 20L30 18L8 22L28 21'); offset-rotate: 0deg; offset-anchor: 0 100%; offset-distance: 0%; opacity: 0; transform: rotate(-38deg); transform-origin: 0 100%; pointer-events: none; }
.cb-answer-sheet__input:checked ~ .cb-answer-sheet__real .cb-answer-sheet__pencil { animation: cb-answer-sheet-write 0.92s linear; }
@keyframes cb-answer-sheet-write {
0% { opacity: 0; offset-distance: 0%; transform: translate(14px, -18px) rotate(-38deg); }
14% { opacity: 1; offset-distance: 0%; transform: rotate(-38deg); }
90% { opacity: 1; offset-distance: 100%; transform: rotate(-38deg); }
100% { opacity: 0; offset-distance: 100%; transform: translate(18px, -14px) rotate(-38deg); }
}
.cb-answer-sheet__wood { fill: var(--wood); }
.cb-answer-sheet__tip { fill: var(--paper); stroke: var(--print); stroke-width: 0.7; }
.cb-answer-sheet__lead-tip { fill: var(--print); }
.cb-answer-sheet__band { fill: var(--print); }
.cb-answer-sheet__text { min-width: 0; white-space: nowrap; font: 700 13px/1.15 Georgia, 'Times New Roman', serif; }
.cb-answer-sheet__foot { position: absolute; left: 30px; right: 14px; bottom: 9px; display: flex; justify-content: space-between; font: 700 8px/1 'Courier New', Courier, monospace; letter-spacing: 0.06em; }
@media (prefers-reduced-motion: reduce) {
.cb-answer-sheet__lead, .cb-answer-sheet__input:checked ~ .cb-answer-sheet__real .cb-answer-sheet__lead { transition: none; }
.cb-answer-sheet__input:checked ~ .cb-answer-sheet__real .cb-answer-sheet__pencil { animation: none; }
}Goal: Create a "Answer sheet" checkbox.
Visual behavior: An exam form with timing marks and a cut corner, the sort you fill in with a soft pencil. Tick it and a pencil scribbles the oval in, going over the lines a bit.
Technique:
- The scribble is one zigzag path drawn in by its dash offset, and the pencil rides offset-path along the same zigzag at the same pace, so its point stays on the line it leaves behind. The strokes run a little past the oval, the way a hurried hand does.
- The cut corner of the form is a clip-path, and the timing marks run down its edge. Pure CSS.
Accessibility: A native checkbox inside a <label>, so "6. I agree to the terms" is its name. The other questions and the marks are aria-hidden, and keyboard focus outlines the oval. Under prefers-reduced-motion the oval fills at once and the pencil never shows.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: neutral only. Black, white and grays, no hues. Use the color values above as written, or my project's closest neutral tokens.
Return a single self-contained component. No dependencies.Goal: Create a "Answer sheet" checkbox.
Visual behavior: An exam form with timing marks and a cut corner, the sort you fill in with a soft pencil. Tick it and a pencil scribbles the oval in, going over the lines a bit.
Technique:
- The scribble is one zigzag path drawn in by its dash offset, and the pencil rides offset-path along the same zigzag at the same pace, so its point stays on the line it leaves behind. The strokes run a little past the oval, the way a hurried hand does.
- The cut corner of the form is a clip-path, and the timing marks run down its edge. Pure CSS.
Accessibility: A native checkbox inside a <label>, so "6. I agree to the terms" is its name. The other questions and the marks are aria-hidden, and keyboard focus outlines the oval. Under prefers-reduced-motion the oval fills at once and the pencil never shows.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: use my project's existing color tokens. The color values above are grayscale placeholders. Map each one by its role (surface, text, border, accent, glow) to my theme, and keep their relative lightness so contrast and the effect still read.
Return a single self-contained component. No dependencies.Paste into Claude, ChatGPT or Cursor after filling in {{USER_STACK}}, {{USER_STYLING}} and {{USER_CONSTRAINTS}}. Neutral keeps the greys you see here; Match my project asks the model to map them onto your own colour tokens.
Free to use in any project, commercial included. MIT-0, no attribution required. Licence
About this component
An exam form with timing marks and a cut corner, the sort you fill in with a soft pencil. Tick it and a pencil scribbles the oval in, going over the lines a bit.
- Dependencies
- None
- Difficulty
- Intermediate
- Triggers
- click, state change, draw
- Techniques
- stroke dashoffset, keyframes, clip path, css transition, svg
- Reduced motion
- Respected
- Theme
- Light and dark (follows your page's color-scheme)
A native checkbox inside a <label>, so "6. I agree to the terms" is its name. The other questions and the marks are aria-hidden, and keyboard focus outlines the oval. Under prefers-reduced-motion the oval fills at once and the pencil never shows.