CozyDevKit · Architecture Reference
Issue No. 01 — Architecture Reference

The Boilerplate
Is Dead.

Angular 21 introduced headless components, signal composition, and zoneless rendering. The old patterns don't work anymore. This is what architecture-level Angular code looks like now.

04Skin LayerCSS only · zero logic
↑ renders
03Headless ComponentsAngular Aria · a11y built-in
↑ composes
02Signal Statesignal() · computed() · effect()
↑ reads
01Domain LayerModels · Rules · Validation
01 — Headless Components

Separate What It Does
From How It Looks.

Angular Aria gives you 8 headless primitives. No styles. No opinions. Just keyboard navigation, ARIA attributes, and focus management. You provide the skin. The component handles the behavior.

CdkComboboxARIA

Keyboard-navigable dropdown with typeahead, auto-complete, and screen reader announcements. Zero visual opinions.

@Component({ selector: 'app-framework-picker', hostDirectives: [CdkCombobox], template: ` <input cdkComboboxTrigger [value]="display()" (input)="filter($event)" /> <ul cdkListbox> @for (opt of filtered(); track opt.id) { <li [cdkOption]="opt"> {{ opt.name }} </li> } </ul> ` }) export class FrameworkPicker { // All state is signals — zoneless safe query = signal(''); options = signal<Framework[]>([]); selected = signal<Framework | null>(null); filtered = computed(() => this.options().filter(o => o.name.toLowerCase().includes( this.query().toLowerCase() ) ) ); display = computed(() => this.selected()?.name ?? this.query() ); }
CdkToggleSIGNAL

Accessible toggle with full keyboard support. The headless logic is one signal. The skin is pure CSS. They never touch each other.

// Headless toggle — pure behavior, no template export function createToggle(initial = false) { const state = signal(initial); const label = computed(() => state() ? 'Enabled' : 'Disabled' ); return { pressed: state.asReadonly(), label, toggle: () => state.update(v => !v), set: (v: boolean) => state.set(v), }; } // Usage in any skin component: // toggle = createToggle(); // <button (click)="toggle.toggle()" // [attr.aria-pressed]="toggle.pressed()">
Signal FormsCOMPOSE

Reactive form() API with signal-driven validation. No FormGroup boilerplate. Computed validity propagates automatically.

const loginForm = form({ email: ['', required, email], password: ['', required, minLength(8)], remember: [false], }); // Computed validity — no manual checks const canSubmit = computed(() => loginForm.valid() && !submitting() ); // Granular field signals loginForm.controls.email.value(); // reactive loginForm.controls.email.errors(); // reactive loginForm.controls.email.touched(); // reactive
Zoneless BootstrapZONELESS

No Zone.js. No monkey-patching. Change detection runs when signals change. The application bootstraps 40% faster.

bootstrapApplication(AppComponent, { providers: [ provideZonelessChangeDetection(), provideRouter(routes, withComponentInputBinding(), withViewTransitions(), ), provideHttpClient( withInterceptors([authInterceptor]), ), ], }); // No zone.js import anywhere // No NgZone. No ApplicationRef.tick() // Signals drive everything.
02 — Signal Composition

Reactive Graphs,
Not Spaghetti.

Angular 21 signals are composable primitives. signal() holds state. computed() derives it. effect() reacts to it. linkedSignal() bridges external sources. resource() fetches async data reactively.

signal()
computed()
effect()
|
resource()
PrimitiveRoleZoneless Behavior
signal()Writable state containerTriggers change detection on .set() / .update()
computed()Derived read-only valueLazy — recalculates only when dependencies change
effect()Side effects (logging, analytics, DOM)Runs in microtask after signal graph settles
linkedSignal()Writable signal synced to external sourceTwo-way bridge between signal and non-signal world
resource()Async data that reloads when deps changeReplaces resolver + ngOnInit + subscribe patterns
03 — Layered Architecture

Four Layers.
Zero Coupling.

Every feature follows the same pattern: Domain defines the rules. State manages signals. Headless composes behavior. Skin renders pixels. Nothing else.

LayerResponsibilityFilesDepends On
04 · Skin CSS, template, visual presentation. No logic, no imports from state layer. *.component.css Headless only
03 · Headless Angular Aria directives, keyboard handlers, ARIA attributes, focus trap. *.headless.ts State only
02 · State Signals, computed values, effects. The reactive graph for this feature. *.state.ts Domain only
01 · Domain Models, interfaces, validation rules, business logic. Pure TypeScript. *.model.ts Nothing
04 — Live Demo

Headless Components,
Running.

These demos implement the headless patterns above. The behavior is framework-agnostic. The skin is pure CSS. Swap the styles, keep the logic.

headless-combobox.component.ts
Select a framework
Selected: none
↑↓ navigate · Enter select · Esc close · Type to filter
headless-toggle.component.ts
Feature toggles (headless)
State: {}
Space/Enter to toggle · Tab to navigate · ARIA pressed
CozyDevKit

This Is How Angular 21
Was Meant to Be Built.

5 interactive tools. 6 cheat sheets. Architecture patterns. $10 one-time.