Page loaded: UI Architecture Notes for Angular Teams

Writing

Notes on building, architecting, and maintaining front-end systems that no longer feel new.

I write about Angular, CSS, responsive design, accessibility, rendering, and long-lived UI decisions shaped by changing requirements, shifting teams, and real business pressure.

Miguel Carino
Focus
Front-end architecture, Angular, team leadership, and stakeholder communication
Experience
25+ years turning enterprise web complexity into maintainable products

Your Design System's Real API Is a CSS Variable

A component's theming surface is a contract whether you designed it or not. Leave it undeclared and consumers will fork the component to get one.

A team copied a button component to change its accent color, and the copy drifted. Exposing a small set of CSS custom properties gives consumers a sanctioned way to theme without forking, without ::ng-deep, and without an input for every color. read more...

Losing the Specificity War, Then Layering Out of It

Specificity escalation has no ceiling, so every override wins until the next one. Cascade layers replace the arms race with an order you declare once.

A design-system rule and an app override kept escalating specificity until nobody could predict the winner. Cascade layers decide by declared order instead of selector weight. Two rules decide whether the migration goes well. read more...

A Route Guard With No Constructor to Inject Into

Converting a class guard to a function left no constructor to inject into. That absence turned out to be the lesson.

Functional route guards are plain functions, so there is no constructor to receive services. inject() reads from the active injector instead, which is why it works inside a guard and throws once the call leaves the injection context. read more...

A Component That Reads the Room

Threading a dark flag through three components that don't care about theming is a design smell. Some differences belong to the surroundings, not the props.

A card needed a different look inside a dark sidebar, and the obvious fix coupled three intermediate components to theming. :host-context() lets a component respond to an ancestor class with no inputs and no JavaScript — with one caveat worth knowing. read more...

A Selection That Went Stale Every Refresh

Some state is derived and writable at the same time. Forcing it into a computed or an effect is what kept breaking the detail panel.

A selected row survived a filter change and pointed at a record no longer in the list. linkedSignal fits state that is derived from a source but still writable by the user — the case computed can't hold and an effect only appears to solve. read more...

CSS Grid That Replaced Half My Media Queries

The card wall had four breakpoints, and none of them knew how much space they actually had. One line of grid asked the container instead of guessing at the viewport.

A stack of breakpoint media queries reflowing a card grid came down to a single intrinsic grid declaration. The layout started responding to available space instead of to guesses about screen width. read more...

When Route Metadata Became Architecture

Every page set its own title in a lifecycle hook, and half of them forgot. The share cards came up blank, the tab titles all said 'App', and the fix wasn't more setTitle calls — it was moving metadata out of the components entirely.

Per-component title and meta calls scattered across a codebase became a routing concern: metadata declared as route data, applied in one place, rendered on the server so crawlers actually see it. read more...

One Service, Two Injectors

The new Angular component added an item to the cart. The old AngularJS view swore the cart was empty — because each framework had quietly built its own copy of the 'shared' service.

In an ngUpgrade hybrid, a service registered in both frameworks becomes two instances with diverging state. The fix is to give it one owner and bridge that single instance across the injector boundary. read more...

The JavaScript I Stopped Writing

A ResizeObserver to make a card respond to its own width. A click-outside handler for a menu. A watcher that toggled a class on a parent when a child changed. The platform grew a native answer for each one, and I deleted the scripts.

A lot of the JavaScript we wrote for layout and for reflecting state in the DOM is now a CSS or HTML feature. Container queries, :has(), the Popover API, cascade layers, and subgrid replace whole categories of glue code — with one that still needs a support caveat. read more...

The Preview Card That Never Filled In

Every link I shared previewed as the same generic card. The per-page title, description, and image existed — but only after JavaScript ran, and the crawlers reading the link never ran it.

On a statically hosted single-page app, whether a page is visible to search and social crawlers is decided at build time, not at runtime. Prerendering bakes the metadata into the HTML the crawlers actually read. read more...