Previous article: The First DOM Wrapper and Scoped CSS
Opening Context
In the previous article, I described how ElementWrap stabilized my daily DOM work and how scoped CSS finally reduced the endless stylesheet collapse.
That phase worked far better than I had expected in real projects. A layered styling approach emerged and kept screens manageable: global design for forms, tables, and base controls was loaded once; page-level or shared partial styling was handled with Razor scoped CSS; and dynamically generated elements carried their HTML and styling together inside ElementWrap.
I also struggled for a while with one practical decision: where scoped CSS should physically live in runtime flow. The solution I settled on was to generate a style tag from ElementWrap and append it to head. It was not perfect, but it was clean enough for my constraints and effective enough to stay in production use.
By then, most DOM-level instability was under control. The next bottleneck was API calls.
The API Chaos Phase
During the jQuery era, I depended on jQuery.ajax and similar helper patterns. When I moved to pure TypeScript, my first step was simple: wrap fetch behind a small Api class. In the beginning, that class was basically a thin fetch wrapper.
Even that tiny abstraction helped. Repeated option definitions were reduced, and request handling became more consistent across screens.
But fetch was never the real problem.
The real problem was that the full procedure was still unstructured: collect form data, transform it, call an API, handle success or failure, then reflect state changes back into the UI. Different screens implemented the same CRUD flow in slightly different ways, and each small divergence created maintenance cost later.
Another factor made this worse. I only started doing end-to-end web development in a serious way after I had already become a solo builder, so I had to discover almost every workflow by myself while still shipping production screens. That naturally amplified complexity and slowed standardization. Calling it a skill gap is fair, but the larger issue was that both learning and delivery had to happen at the same time, in the same codebase.
The timing also mattered. As smartphones spread quickly, mobile support became unavoidable, and that pressure landed directly on the same fragile architecture. I tried jQuery Mobile early, and it helped me move quickly at first, but in practice it pushed me toward maintaining separate screen structures for PC and mobile contexts. That solved one problem and created another, because divergence between two UI structures increased maintenance overhead and made behavior consistency harder to keep.
Responsive design ideas probably already existed in some form, but at least in the practical range I could easily reach through everyday web searches and books at that time, I did not have a reliable path to adopt them with confidence.
The Ugly Phase: Button-Driven JSON Assembly
Before I introduced proper form interception, many screens were still driven by button click handlers. Those handlers collected input values manually, assembled JSON objects manually, and sent them through API calls.
There were reasons for this. Some screens changed fields dynamically by configuration. I was optimizing for delivery speed. I was also still searching for repeatable patterns under pressure, while teaching myself how to structure a full web stack in parallel and juggling PC and mobile behavior expectations.
Still, this was one of my weaker patterns. I abandoned many experiments, threw away a lot of code, and rewrote screens with different approaches. I do not remember every attempt, because there were too many trial branches that never deserved to survive.
The Turning Point: Classifying Forms by Access Type
The turning point came when I stopped treating all forms as the same thing.
Forms differ by access type and intent.
Search forms and edit forms may look similar on screen, but their responsibilities are different. Once I accepted that distinction, architecture decisions became much clearer. Search forms should use query strings, avoid API dependence, and preserve URL state. Edit forms should use API calls, run fully through AJAX, and avoid server-side re-render dependence for data expansion.
Before this classification, I often rendered data on the server for edit screens and still implemented API-side logic for the same screen behavior. That duplicated transformation logic in two places.
This was a hidden defect generator, and it was unacceptable.
Inheritance-Based Form Architecture
To resolve this, I introduced inherited form classes instead of leaving each screen to improvise.
I established a base form class, a query-string-oriented form class, an API-oriented form class, and mode-aware variants for new and edit handling. I am intentionally describing the structure conceptually rather than as exact class names from that period, because naming and details evolved over time.
After some additional revisions, another practical requirement became explicit. In most screens, it was natural to define method and action directly in HTML form markup. But some screens needed TypeScript-side control because method or target URL had to switch dynamically by runtime state. I needed both styles to coexist without creating separate form ecosystems.
So the architecture evolved toward a dual boundary: declarative defaults in HTML, plus controllable override points in TypeScript when dynamic behavior was truly required. As usage areas expanded, the form layer became more flexible, but the contract stayed consistent.
What matters is the boundary that was created.
This was the phase when my TypeScript common classes finally started to feel cohesive rather than accidental.
Dramatic Impact
The impact was immediate.
CRUD implementation time dropped. Consistency improved. Server and client responsibilities stopped duplicating as often. More importantly, I could choose the access style, method, and endpoint control point that fit each screen’s purpose without breaking implementation consistency. Some screens stayed mostly declarative through form markup, while others used TypeScript-side switching for dynamic transitions, and both approaches remained inside one architectural model.
The key benefit was flexibility without fragmentation.
The Moment I Considered Publishing
Around this point, I noticed something had changed.
My TypeScript foundation had structure. The DOM layer had coherence. The form layer had classification. API access had a standard boundary.
It no longer felt like a pile of utilities. It felt like a system.
That was the first time I seriously thought, Should I publish this?
The Reality: Overload and Frustration
At the same time, my reality was harsher than the architecture progress. I was not only developing systems. I was also managing internal operations, handling recruitment, supporting business process redesign, sometimes supporting field operations, and even taking on sales activities when needed. The work volume had already crossed a reasonable one-person boundary. There was no universe in which I had the time to polish and publish anything.
The publication idea was postponed, not abandoned.
Closing Reflection
The form architecture phase was where scattered techniques finally converged.
It was not Cotomy yet.
But it was no longer a sequence of isolated experiments. It was a direction.
Development Backstory
This article is part of the Cotomy Development Backstory, which traces how Cotomy’s architecture emerged through real project constraints.
Series articles: Building Systems Alone , Early Architecture Attempts , The First DOM Wrapper and Scoped CSS , API Standardization and the Birth of Form Architecture, Page Representation and the Birth of a Controller Structure , The CotomyElement Constructor Is the Core , Dynamic HTML Boundaries in CotomyElement , Reaching Closures to Remove Event Handlers Later , and The Birth of the Page Controller .
Previous article: The First DOM Wrapper and Scoped CSS Next article: Page Representation and the Birth of a Controller Structure