Sheet 006 — 2026
Building Eskiz, chapter one: the number input
Before a model becomes a building, a chair, or a very expensive mistake, someone changes a number. We should probably make that part good.
01 / 10
The most important box in the room.
In the previous article, we introduced Eskiz and the problem of making 3D modeling easier to change. This is chapter one of how we are approaching it. We are starting with the number input.
I would argue it is the most important component in a CAD application. Width, height, radius, angle, spacing: sooner or later, a design decision passes through this small rectangle. A model can look magnificent while changing its width remains an administrative procedure.
Take a panel, 120 millimeters wide. Make it 128. Easy. Now make it eight millimeters wider without doing the addition yourself. Enter a measurement in centimeters. Keep its width related to another part. Select three different panels and add eight to each. Still the same little box.
That is the story of our input: each apparently small convenience introduces a responsibility. The examples below use the actual shared component, with small local demonstrations around it. The panel drawings are illustrations, not a geometry kernel.
01 / Start with 120
02 / 10
A number is not the same thing as an edit.
The native HTML number input is a useful starting point. It accepts numbers, supports bounds and steps, and may provide spinner buttons. It does not know what 12cm or 120 + 8 means. That is outside its job description.
Replacing it with a text field gets us the characters. It also gets us the responsibility for everything those characters mean. Halfway through typing 120 + 8, the text says 120 +. That is an unfinished thought, not a new panel width.
So we separate the text being edited, a usable preview, and the committed value. Plain numeric edits can preview immediately; an expression can show its evaluated result without committing every intermediate keystroke. Enter or blur settles a valid edit. A failed expression keeps its text so you can repair it. Quietly replacing your mistake with the old value would remove the evidence.
Try 120 + 8, then 120 +. The first has an answer. The second deserves a cursor, not a funeral.
Native behavior reference: MDN’s number input documentation.
02 / Text, preview, committed value
03 / 10
Even focus has a job.
Typing a replacement is the first job. It is usually faster to write the new number than to place a cursor between the old digits. So focusing a plain value leaves it visible in a lighter color, ready for you to type straight over it. No selecting or clearing first. If you do want to edit the old value, an arrow key brings it into the field. Leaving without typing changes nothing.
A formula needs different treatment. At rest, 10 + 5 normally displays 15. On focus, its source returns. Keyboard focus selects the whole source; pointer focus can place the caret, with select-all-on-first-click available when the host wants it. We have a regression test for the alternative: the source appeared, the caret landed at the end, and typing 100 produced 10 + 5100. An ambitious interpretation of a small edit.
The visual design follows the same distinction. At rest, one quiet box. On engagement, the scrub handle reveals its seam; focus adds an internal bar, without an extra outline. The seam opens inside the existing footprint, so the neighboring fields do not shuffle around. The unit stays its own target. A little movement tells you where the handles are; the inspector does not need to perform a dance.
03 / One box, three jobs
04 / 10
A drag is one decision.
Sometimes you know the answer is 128. Sometimes you know the panel looks slightly too narrow. Typing serves the first situation; dragging the label serves the second. We use the label as a larger scrub target instead of adding tiny spinner arrows.
The same value has several gears: Alt for one tenth of a step, Shift for ten steps, and Command or Control with Shift for a hundred. A zero value must still move. Fine movement must keep its decimals. These sound like details until your mouse has crossed the desk and the number has barely noticed.
During a drag, the model can preview each change. On release, the field reports one commit. That lets the owner create one undo entry for the gesture. Escape cancels the scrub and restores its starting value. The field supplies the boundary; the application owns the history. In this example, a tiny history stack makes that distinction visible.
04 / Explore now. Commit once.
Drag W. Alt ×0.1 · Shift ×10 · ⌘/Ctrl + Shift ×100. Escape cancels a drag.
05 / 10
The unit is part of the meaning.
Our panel is 120 millimeters wide. Showing 12 centimeters must not make it ten times larger. Typing 12cm into a millimeter field converts the quantity; it does not paste a different label beside 12. A length cannot become an angle just because both fit in the same box.
There is another distinction we had to make explicit: changing an authored unit and changing a display preference. The field supports both. In the demo, the unit cap changes only the display. The stored magnitude, expression source, and edit unit remain millimeters. Enter the field and the authored unit returns visibly.
The same goes for precision. You might prefer to read 12.35 cm. That is not permission to replace the model’s 123.456789 mm with a rounded value. Display rounding and edit rounding have separate controls. A tidier inspector should not quietly become a manufacturing operation.
05 / Change the view, keep the quantity
Click the unit cap to cycle mm → cm → in. Focus to edit in millimeters. Type 12cm and press Enter.
06 / 10
Keep the calculation, too.
A width of 128 tells us the result. A width of 120 + 8 also tells us how we arrived there. A reference such as @panel.width / 2 says something stronger: this dimension depends on another one.
We keep the source. The result is usually what you see at rest; focus reveals the expression, syntax highlighting, and a live result preview. Type @ to choose from names supplied by the host. Completion stays attached to the input without taking focus away from the text you were writing. Escape dismisses the suggestions before a second Escape abandons the edit.
Then comes the less obvious decision: dragging a formula must not silently destroy it. Nudge 120 + 8 by two and the field folds the adjustment into the source. With a reference, the reference survives. Turning the result into a plain number is an explicit decision, not a side effect of touching the handle.
The field can ask what @panel.width means. It cannot decide which objects depend on it, detect every cycle, or propagate changes through a document by itself. Those belong to the owning system. This demo provides one fixed reference; it is a field demonstration, not a miniature dependency engine.
06 / The answer, and the reason
Drag W or use ↑/↓. The calculation stays a calculation. @panel.width is fixed at 240 mm in this example.
07 / 10
Seven ways to say 120.
The digits alone do not tell you whether a value was typed, inherited, calculated here, or controlled elsewhere. We represent seven readings: default, literal, overridden, expression, driven, mixed, and error.
Default means nobody set it. Literal means somebody did. Overridden means the authored value differs from a known default, so there is something useful to reset. Expression means calculated here. Driven means another source owns it. Mixed means the selected objects disagree. Error means it could not be resolved.
These are not seven colors. A formula has a marker. A driven value is read-only and identifies its source. An override can offer a revert button. Mixed says Mixed. An error retains the troublesome text and exposes an invalid state. The distinction should survive without the color.
Nor are all seven stored independently in the document. Mixed comes from the selection. Overridden comes from comparing with a default. Error comes from evaluation. Storing those conclusions as if they were permanent facts would create another set of numbers to keep honest.
07 / Seven readings of a field
Default
Nobody set it; still editable.Literal
A directly authored value.Overridden
Different from its 100 mm default.Expression
Calculated here; focus shows the source.Driven
Read-only; owned by panel.width.Mixed
Three selected values disagree.Error
Unresolved source; text survives.08 / 10
Mixed is not zero.
Now select three panels: 40, 60, and 100 millimeters wide. Showing zero would be false. Showing the first panel’s width would merely be false with more confidence.
The field says Mixed. Type 80 and all three become 80. Type + 8 and they become 48, 68, and 108. A signed subtraction works the same way: -40 subtracts forty from each. Dragging the label also applies a shared delta, keeping the differences between the panels. To assign an absolute negative value, use parentheses: (-40). Relative input is evaluated against each original value; the batch succeeds together or is refused together. The reader should never have to discover halfway down the selection that only some of the edit happened.
That still leaves ownership clear. The inspector chooses the targets. The field evaluates and reports the proposed values. A linked width-and-height pair is another coordinator’s job: each individual field should not secretly know about its neighbor. Small controls become difficult when we give them the whole application to manage.
08 / One edit, three originals
Drag W to adjust all three together. Try +20, then -40. Type 80 to set all widths to 80; (-40) explicitly sets all to negative forty.
09 / 10
The awkward cases are the component.
An inspector gets narrow. A formula gets long. We let the content borrow space from the caps before sacrificing its digits. On fine pointers, hover reveals the fuller anatomy again; keyboard focus can recover a collapsed unit control. Touch keeps the anatomy whole because there is no hover to come back through. Below a useful width, the honest answer is a wider inspector.
I am not very happy with the narrow-field behavior yet, especially with long formulas. Keeping the whole expression readable and easy to edit in a small inspector is still something I am thinking through. The example below shows where we are, rather than a decision I consider finished.
Limits also need meaning. A soft limit preserves an out-of-range entry and warns. A hard limit constrains it. An angle may wrap through 360 degrees; a hinge may have actual stops. We cannot infer those product decisions from the fact that the value happens to be numeric.
Even a mathematically valid answer can be wrong for a document. The host gets a chance to refuse a commit before it is applied. A refused edit keeps its draft and explanation. We already had a bug where a value preview changed before the owner rejected it, leaving the field and document disagreeing. It is considerably easier to trust a refusal when the object has not moved anyway.
09 / The edges of the happy path
10 / 10
Back to the model.
The complexity is in the combinations: an expression while scrubbing, a unit preference while focused, an invalid draft when Apply is pressed, a read-only value reached by keyboard instead of mouse. We test those transitions because a screenshot of the resting field cannot tell us whether any of them works.
This is why we are starting the Eskiz series here. Easier modeling is partly the accumulation of small, dependable interactions: type what you mean, explore without losing your starting point, keep the relationships you created, and understand why a change was refused.
AI collaboration will still need those properties. Whether a person types a dimension or an agent proposes one, we need to inspect its source, adjust it, and trust what gets committed. The number input does not solve the whole CAD application. It does give us a very small place where the promises have to become real.
The production component is much richer than these examples. We have kept the demos focused so each decision is easy to try; there are many more advanced features to discover in the Eskiz app. Even the small rectangle has things left to show you.
We wanted to change a panel’s width. We now have opinions about the first arrow key. This is how it starts.

