Previous article: Early Architecture Attempts
From jQuery Removal to Pure TypeScript
In the previous article, I described the phase where I still depended on a server template plus TypeScript plus jQuery clone flow. I had already felt that this combination was fragile, but at that point I still treated it as a necessary compromise.
ElementWrap started as a very small utility. It was not born as a dynamic UI engine. It was just a thin wrapper around HTMLElement. The constructor took an HTMLElement instance, and the early methods only existed to reduce repetitive direct DOM handling that kept appearing across screens. Looking back, this small class was the direct ancestor of what later became CotomyElement.
The initial objective was very practical: at first, I was not trying to discard the server-rendered find and clone flow. I wanted to do that same flow in a simpler and safer way than jQuery, with type constraints helping me avoid fragile handling. I was not trying to define a grand front-end philosophy. I simply wanted pure TypeScript to own more of the dynamic behavior so that the control flow stayed in one language and one mental model.
That single shift made my work sessions less fragmented. Instead of tracing jQuery selections across templates, I could hold the element reference directly and move through logic with types. It did not eliminate selector-related risks overnight, but it reduced the amount of guesswork during routine changes. The first version of ElementWrap was almost boring, and that was exactly why it helped.
CSS Collapse and Design Obsession
The harder problem appeared in CSS.
As my internal systems grew, stylesheets expanded in a way that was no longer an aesthetic inconvenience. It became an operational problem. I had to spend too much time checking whether one screen tweak damaged another, and naming conventions alone were not containing that risk.
To be fair, part of this was my own CSS management quality at the time. I tried several patterns to improve it, and some of them did help for a while. But as revisions accumulated across years of real operations, those local improvements were not enough to prevent eventual breakpoints.
Long before this period, I had built a custom control set in Swing. That experience stayed with me. I had learned that UI control is not vanity. It directly changes how calmly and efficiently people can do daily work. In business systems, users repeat the same screens for years. Minor visual friction accumulates into fatigue.
I have always believed that even slight improvements in visual clarity can lift mood, and mood changes productivity more than many teams want to admit. People think this is a soft topic, but in repetitive operations, the emotional baseline matters.
And if I am being completely honest, a slightly polished interface seems to buy you a small amount of goodwill when something breaks.
That observation was not theoretical. I learned it through repetition, and it convinced me that interface control — visual structure and interaction behavior together — was operational, not cosmetic.
Why Existing Templates Did Not Work
I tried using off-the-shelf design templates in multiple projects. They were not useless, and I did get measurable improvements by reorganizing naming, splitting style concerns, and tightening conventions. But once repeated feature revisions piled up, the control quality degraded again, and I could not tolerate that cycle.
The core problem was not that styling was difficult. The core problem was distance. The element that I needed to style and the CSS definition that affected it were often too far apart in both file structure and ownership. By the time I touched a rule, several other screens might already depend on it in ways that were not obvious.
I also tried splitting CSS files more aggressively. That improved readability for a while, but it did not solve growth. The same collision patterns returned when projects crossed a certain size and revision density.
I considered SCSS too. It offered useful organization tools, but at that time it felt like additional structural layering around the same underlying problem. I could nest, import, and reuse, yet the design-to-target distance still remained. The syntax was better, but the responsibility model was unchanged.
Realization: The Distance Problem
The turning point was simple to state and difficult to ignore.
The real issue was that the applied design and the target element were too far apart in structure and responsibility.
Once I recognized that, many earlier frustrations became easier to classify. Selector conflicts, naming inflation, and cautious refactors were symptoms, not root causes. I had been trying to stabilize behavior while design intent and DOM ownership lived in separate places.
That insight changed everything, because it gave me a criterion for architecture decisions: reduce distance between what I build and how it is styled.
Inspiration from Razor Pages Scoped CSS
Around that time, I encountered Razor Pages with the cshtml and cshtml.css pairing model.
What struck me was not novelty for its own sake. It was the practical feeling of locality. CSS stayed near the page it targeted. The stylesheet stayed small enough to reason about. Side effects felt limited by default, not by discipline alone.
For someone drowning in shared stylesheet sprawl, that experience felt almost magical. Not magical in a technical sense, but in the sense that a long-standing maintenance pressure suddenly had a comprehensible shape.
I do not mean it solved every concern automatically. But it showed me a direction where blast radius could be reduced by structure, not only by naming effort.
Extending ElementWrap
ElementWrap then expanded beyond the original HTMLElement-only constructor.
At first, it only accepted HTMLElement. Later I added another pattern that accepted html and css together. There was no elegant unified overload design at that stage. I was building for immediate internal needs, not for publication quality.
So for a while, two constructor patterns coexisted:
- HTMLElement
- html + css
In the initial implementation, scoped CSS could also be attached later depending on the situation. I had implemented it that way because I thought it would be useful to change how state was expressed while the UI was running. As I explain later, that turned out to be a bad pattern, but this period was full of countless bad design choices and countless incremental improvements made through daily development work.
Note: in the final CotomyElement design, I restricted scope ID setup to constructor time only. The reason was structural. If scope styling could be attached later, it would also allow scoped CSS to be applied to server-generated HTML after the fact, and that would reintroduce the same distance problem from a different direction. I concluded that CSS should not be attachable later if I wanted to preserve the element-style locality that this approach was meant to enforce.
The scope identifier in that era was [scope]. In current CotomyElement, the marker is root-based, but back then [scope] was the practical anchor I could implement quickly.
The scoping approach itself was straightforward. I prefixed selectors with the scope attribute so rules only applied to the intended element tree. It was not elegant in a textbook sense, and it could produce heavy-looking rules. But in a private system, that tradeoff was acceptable. Predictability and speed mattered more than stylistic purity.
I did not yet have a formalized abstraction boundary or polished naming conventions. Still, for daily delivery, this was the first time CSS behavior felt governable instead of constantly defensive.
Dramatic Effect
The effect was immediate enough that I abandoned the old clone-template flow soon after.
Most dynamic elements started being defined directly in TypeScript, with their CSS kept close to the same construction context. That shift reduced the number of files I had to traverse for routine screen changes and made impact estimation much faster.
This was still not a framework. It was still far from public quality. If I showed that code to a broad audience, I would have had many things to explain and even more to rewrite.
But productivity improved dramatically, and that was the metric I cared about at the time. I could iterate UI behavior faster, isolate style changes better, and recover from mistakes with less collateral damage.
In hindsight, this was less about inventing something new and more about finally aligning the structure with the kind of work I was actually doing every day.
Why Not React?
People may ask why I did not adopt React or similar tools at that stage.
The short answer is scale and scope.
Frameworks like React can address many of the same classes of problems, especially when teams, screens, and state interactions grow beyond what ad hoc structure can safely handle. I do not deny that. Honestly, some systems where I used ElementWrap would likely have benefited more from React, but in the daily pressure of delivery I could not justify paying that learning cost for only one part of the workload.
But I did not need that scale in my immediate context. My dominant pain was not ecosystem breadth. It was the gap between element ownership and style ownership. I needed to narrow scope first and regain local control. ElementWrap with scoped CSS was enough to do that for my environment.
So this was not a rejection of React. It was a decision to solve the smallest structural problem that was actively slowing my delivery.
The Endless Shift of Problems
ElementWrap worked much better than I expected.
At that time, I had no intention to publish it or shape it into a general-purpose tool. It was a private response to practical pressure. Yet once this structural problem became manageable, another set of problems became visible, especially around boundaries, consistency, and what should be considered reusable versus screen-specific.
That pattern has repeated throughout my system development life. When one irrationality is reduced, another appears from a different angle. The work is not a straight path to completion. It is continuous confrontation with new irrationalities, each one asking for a better boundary than the previous one.
AI will likely influence part of this process over time. It can accelerate prototyping, verification, and even some architectural exploration. But I do not think this confrontation disappears soon, because the hardest problems are usually about responsibility and tradeoff, not only code generation.
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: Early Architecture Attempts Next article: API Standardization and the Birth of Form Architecture