Mycelium

I · Meaning

What Happens When They Finish?

A form is a mechanism. It isn't what the person is doing.

I learned this the slow way, building a WhatsApp template builder.

I reached for the obvious shape without noticing I'd reached for it: a column of labeled inputs. Header. Body. Footer. Buttons. A small preview off to the side. It looked like every form I'd ever made, and that was exactly the problem, because the person using it wasn't entering data. They were building a message, something with a face, something that would land in a stranger's WhatsApp and be judged the second it arrived. And they couldn't see what they were making until they stopped making it and clicked preview. The form stood between the person and the object the whole time.

Here is what that looked like from the chair. In the first version, the person saw a stack of boxes. One labeled Header, where they typed a title. One labeled Body, a plain text box, where the message went, with bold words marked by asterisks and the customer's name as a placeholder in curly brackets. Then Footer, then Buttons, each button another small box with its label typed in. The message itself, the thing the customer would actually receive, sat small to the side. Every change was a round trip: type in a box, look across at the preview, see that a line broke badly or a button's label was too long, go back to the box.

In the second version, the phone was in the middle of the screen, with the message on it as it would arrive. You clicked the text in the bubble and typed right there. Bold looked bold. A button you added appeared as a button, under the bubble, where the customer would tap it. The boxes didn't disappear. They shrank to a few controls at the edge, for whatever can't be seen on the message itself.

Nothing about the data changed. The same header, body, footer and buttons were saved, field by field. Everything about the work did. In the first version, the person was describing a message and hoping the description would come out right. In the second, they were making the message. Editors have had a name for that for decades: WYSIWYG, what you see is what you get. It's the difference between a word processor where you typed a code for bold and printed the page to see if it worked, and one where bold was simply bold on the screen.

<figure style="margin:1.6rem 0;text-align:center"><svg width="400" height="292" viewBox="0 0 400 292" role="img" aria-label="the same message template made twice: a column of fields for name, language, header, body, footer and buttons with a small live preview on the side; and the message itself edited in place, with the header selected, a name variable inside the text, a cursor, and empty slots for buttons"><rect x="6" y="6" width="214" height="262" rx="8" fill="none" stroke="#d8d2c8" stroke-width="1"/><text x="16" y="21" font-family="Inter, system-ui, sans-serif" font-size="8.5" font-weight="600" fill="#3a3631" text-anchor="start">Create template</text><text x="16" y="32" font-family="Inter, system-ui, sans-serif" font-size="6" font-weight="400" fill="#8f887d" text-anchor="start">Set up · Edit · Submit for review</text><text x="16" y="48" font-family="Inter, system-ui, sans-serif" font-size="7" font-weight="600" fill="#3a3631" text-anchor="start">Name</text><rect x="16" y="52" width="88" height="16" rx="3" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="21" y="62.5" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="6.5" font-weight="400" fill="#3a3631" text-anchor="start">order_ready</text><text x="110" y="48" font-family="Inter, system-ui, sans-serif" font-size="7" font-weight="600" fill="#3a3631" text-anchor="start">Language</text><rect x="110" y="52" width="44" height="16" rx="3" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="115" y="62.5" font-family="Inter, system-ui, sans-serif" font-size="6.5" font-weight="400" fill="#3a3631" text-anchor="start">English</text><text x="16" y="82" font-family="Inter, system-ui, sans-serif" font-size="7" font-weight="600" fill="#3a3631" text-anchor="start">Header</text><rect x="16" y="86" width="138" height="16" rx="3" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="21" y="96.5" font-family="Inter, system-ui, sans-serif" font-size="6.5" font-weight="400" fill="#3a3631" text-anchor="start">Your order is ready</text><text x="150" y="96.5" font-family="Inter, system-ui, sans-serif" font-size="6" font-weight="400" fill="#8f887d" text-anchor="end">19/60</text><text x="154" y="111" font-family="Inter, system-ui, sans-serif" font-size="6" font-weight="400" fill="#8f887d" text-anchor="end">+ Add variable</text><text x="16" y="126" font-family="Inter, system-ui, sans-serif" font-size="7" font-weight="600" fill="#3a3631" text-anchor="start">Body</text><rect x="16" y="130" width="138" height="34" rx="3" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="21" y="140" font-family="Inter, system-ui, sans-serif" font-size="6.2" font-weight="400" fill="#3a3631" text-anchor="start">Hi {{1}}, your prints are ready</text><text x="21" y="149" font-family="Inter, system-ui, sans-serif" font-size="6.2" font-weight="400" fill="#3a3631" text-anchor="start">for pickup until 6pm.</text><text x="150" y="159" font-family="Inter, system-ui, sans-serif" font-size="6" font-weight="400" fill="#8f887d" text-anchor="end">52/1024</text><text x="16" y="173" font-family="Inter, system-ui, sans-serif" font-size="6.5" font-weight="600" fill="#8f887d" text-anchor="start">B I S &lt;/&gt;</text><text x="154" y="173" font-family="Inter, system-ui, sans-serif" font-size="6" font-weight="400" fill="#8f887d" text-anchor="end">+ Add variable</text><text x="16" y="188" font-family="Inter, system-ui, sans-serif" font-size="7" font-weight="600" fill="#3a3631" text-anchor="start">Footer</text><rect x="16" y="192" width="138" height="16" rx="3" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="21" y="202.5" font-family="Inter, system-ui, sans-serif" font-size="6.5" font-weight="400" fill="#3a3631" text-anchor="start">Reply STOP to opt out</text><text x="150" y="202.5" font-family="Inter, system-ui, sans-serif" font-size="6" font-weight="400" fill="#8f887d" text-anchor="end">21/60</text><text x="16" y="224" font-family="Inter, system-ui, sans-serif" font-size="7" font-weight="600" fill="#3a3631" text-anchor="start">Buttons</text><rect x="16" y="228" width="64" height="16" rx="3" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="22" y="238.5" font-family="Inter, system-ui, sans-serif" font-size="6.5" font-weight="400" fill="#3a3631" text-anchor="start">+ Add button</text><text x="162" y="38" font-family="Inter, system-ui, sans-serif" font-size="6.5" font-weight="600" fill="#8f887d" text-anchor="start">Preview</text><rect x="162" y="42" width="50" height="96" rx="4" fill="#eeeae3"/><rect x="166" y="48" width="42" height="58" rx="3" fill="#fff"/><rect x="170" y="54" width="30" height="4" rx="2.0" fill="#c9c2b6"/><rect x="170" y="63" width="34" height="3" rx="1.5" fill="#e0dad0"/><rect x="170" y="69" width="30" height="3" rx="1.5" fill="#e0dad0"/><rect x="170" y="77" width="22" height="2.5" rx="1.25" fill="#eeeae3"/><line x1="166" y1="86" x2="208" y2="86" stroke="#d8d2c8" stroke-width="1"/><rect x="176" y="94" width="22" height="3" rx="1.5" fill="#d9b8b3"/><rect x="228" y="6" width="166" height="262" rx="10" fill="none" stroke="#d8d2c8" stroke-width="1"/><text x="238" y="21" font-family="Inter, system-ui, sans-serif" font-size="8.5" font-weight="600" fill="#3a3631" text-anchor="start">Create template</text><rect x="236" y="30" width="150" height="150" rx="8" fill="#efe9df"/><rect x="244" y="38" width="134" height="106" rx="7" fill="#fff"/><text x="252" y="53" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="700" fill="#3a3631" text-anchor="start">Your order is ready</text><rect x="248" y="43" width="126" height="14" rx="3" fill="none" stroke="#7a1f1f" stroke-width="1" stroke-dasharray="3 2"/><text x="252" y="72" font-family="Inter, system-ui, sans-serif" font-size="7.2" font-weight="400" fill="#3a3631" text-anchor="start">Hi</text><rect x="263" y="64" width="32" height="11" rx="5.5" fill="#f2e4e1"/><text x="279" y="71.8" font-family="Inter, system-ui, sans-serif" font-size="6.5" font-weight="600" fill="#7a1f1f" text-anchor="middle">name</text><text x="296" y="72" font-family="Inter, system-ui, sans-serif" font-size="7.2" font-weight="400" fill="#3a3631" text-anchor="start">, your prints are</text><text x="252" y="83" font-family="Inter, system-ui, sans-serif" font-size="7.2" font-weight="400" fill="#3a3631" text-anchor="start">ready for pickup until 6pm.</text><line x1="350" y1="76" x2="350" y2="86" stroke="#7a1f1f" stroke-width="1.2"/><text x="252" y="97" font-family="Inter, system-ui, sans-serif" font-size="6.3" font-weight="400" fill="#8f887d" text-anchor="start">Reply STOP to opt out</text><text x="372" y="109" font-family="Inter, system-ui, sans-serif" font-size="6" font-weight="400" fill="#8f887d" text-anchor="end">16:03</text><line x1="244" y1="114" x2="378" y2="114" stroke="#d8d2c8" stroke-width="1"/><text x="311.0" y="132" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="600" fill="#7a1f1f" text-anchor="middle">Track order</text><circle cx="368" cy="91" r="6" fill="#eeeae3"/><text x="368" y="93.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#3a3631" text-anchor="middle">+</text><rect x="254" y="154" width="36" height="16" rx="4" fill="none" stroke="#8f887d" stroke-width="1" stroke-dasharray="3 2"/><text x="272" y="165" font-family="Inter, system-ui, sans-serif" font-size="6.3" font-weight="400" fill="#8f887d" text-anchor="middle">+ call</text><rect x="296" y="154" width="36" height="16" rx="4" fill="none" stroke="#8f887d" stroke-width="1" stroke-dasharray="3 2"/><text x="314" y="165" font-family="Inter, system-ui, sans-serif" font-size="6.3" font-weight="400" fill="#8f887d" text-anchor="middle">+ link</text><rect x="338" y="154" width="36" height="16" rx="4" fill="none" stroke="#8f887d" stroke-width="1" stroke-dasharray="3 2"/><text x="356" y="165" font-family="Inter, system-ui, sans-serif" font-size="6.3" font-weight="400" fill="#8f887d" text-anchor="middle">+ reply</text><rect x="236" y="192" width="150" height="16" rx="4" fill="#eeeae3"/><text x="242" y="203" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="6.5" font-weight="400" fill="#3a3631" text-anchor="start">order_ready</text><rect x="236" y="216" width="92" height="18" rx="5" fill="#3a3631"/><text x="282" y="228" font-family="Inter, system-ui, sans-serif" font-size="7" font-weight="600" fill="#fff" text-anchor="middle">Send for approval</text><text x="113.0" y="284" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="400" fill="#8f887d" text-anchor="middle" font-style="italic">fields with a live preview</text><text x="311.0" y="284" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="400" fill="#8f887d" text-anchor="middle" font-style="italic">WYSIWYG</text></svg><figcaption style="font-size:.8rem;color:#a8a298;font-style:italic;margin-top:.4rem">The same template, two ways: fields with a live preview beside them, and the message edited in place (WYSIWYG).</figcaption></figure>

It left me suspecting that a form is where what's being made gets lost most easily, and that "form" is a word we use for two different things because they both end up as <form> in the code.


What are we actually asking for?

Consider the range of what we ask people to provide. Who are you? Where do you live? When are you available? How much do you want to spend? Who is on your team? What message do you want to send? What happened when the software broke? Which permissions should this employee have?

These are completely different concepts. Yet we routinely translate all of them into the same shape:

Label → rectangle. Label → rectangle. Label → rectangle.

A person becomes six inputs. A company becomes eight. A schedule becomes fourteen selects. A message becomes a textarea. A budget becomes a number field. An address becomes a stack of strings.

We've taken structured, meaningful concepts and removed almost all of their structure from the interface.


The schema becomes the form

If the first question is what fields do we need?, the database schema quietly becomes the form structure, the form structure becomes the information architecture, and the person ends up operating your data model.

If the first question is what does the person want to do?, a different set follows: are they making something, or sending something? What does it naturally look like? Which properties actually matter to them? What states can it enter? What does success look like? And only then: which controls help them do that?

The database needs fields. The person doesn't. Whenever possible, let them manipulate the object itself instead of a description of it. Not openingTime and closingTime, but show me time and let me mark when we're open. Not teamMembers[], but show me my team and let me add someone. Not permissions[], but show me what this person can reach and let me change it.


Two kinds of form

I like to sort forms into two types, by what's true when the person finishes.

The form makes an object. I created a contact and it's in the list now. I changed the business hours and they read differently. I added a team member, a template, an invoice, a rule for when the assistant answers. Whether the form started blank or started full of the current values, when I'm done something in the product exists or has changed, and I'm looking at it. The form was a way of acting on the product's world.

The form makes an event. Contact us. Report a bug. Subscribe. Request a demo. Apply for access. I filled it, I sent it, it went somewhere I'm not, and the form is blank again, ready for the next person. Nothing of mine stays on the screen. The form was a way of handing something over.

Both can start empty. The difference is at the end, in what I can point to: an object, something that now exists here and that I can see, or an event, something I handed over that went somewhere else.

This line is older than the web. Shneiderman (1983), setting out the styles of interaction a person can have with a computer, separated form fill-in, predefined fields you enter data into, from direct manipulation, the idea underneath WYSIWYG, where the object of interest stays visible the whole time, you act on it directly, and every change shows at once and can be undone. The event form is form fill-in. The object form can be built either way: as fill-in, where the person describes the thing beside a preview of it, or as direct manipulation, where they work on the thing itself.

| The form makes… | When I finish… | |---|---| | an object | something here exists or has changed, and I can see it | | an event | it happened, it went somewhere else, the form resets |

The first kind is where I think most forms go wrong, because the object being made or changed can often become the interface itself. If I'm building a message, show me the message. If I'm setting when the assistant answers, show me the sentence your assistant answers customers when nobody on your team is available with the meaningful parts editable, not a switch labeled Enabled. If I'm creating a profile, show the profile forming, and its state as it forms: what's missing, what's ready. A conventional form says describe it and I will construct it. This kind of form can say here it is. Make it. And where what I'm changing is behavior rather than an object, the change is invisible at the moment I make it (a rule fires later, a permission locks someone out next week), so this kind owes me one more thing: what it touches, when I'll see the result, and what breaks if I get it wrong.

The second kind is where the traditional form earns its place, because there's a clear before and after: I have something to say, I send it, it's received. But events aren't all alike either, and the difference is what happens after I press send. A newsletter signup is fire and forget. I give my email, and there's nothing left for me to follow. A bug report is an event that opens a wait. Somewhere it becomes a ticket on someone else's screen, but for me it becomes a question: has anyone seen it, is it being fixed, will someone answer? So that form doesn't end at "received". It owes me a way to know what happened next. A signup can be an email field and a verb. A bug report needs where, what, screenshots, steps, and a promise about what comes back.


Where this bends

None of this means text fields are wrong. Sometimes a text field is exactly right. An email address is text. A legal company name is text. A long account of a bug may genuinely belong in a textarea. Familiarity matters, accessibility matters, precision and keyboard operation matter. The point was never don't use inputs. It's narrower: don't use an input because the data model has a field. Understand the concept first; choose the interaction second. Sometimes it's a text field. Sometimes a calendar, a timeline, a sentence, a scrubbable number. Sometimes it won't look like a form control at all. And fields with a live preview beside them are sometimes the better of the two: when much of what's being set never shows in the result (a name, a language, a category), when every part has limits the person needs to see counted, or when they work fastest tabbing through fields they already know.