Create account
Form field + input + button
Composed in context.
Forms, navigation, data, feedback — each primitive stitched into a real scene. Type into the inputs, toggle the switches, flip the theme: every tile is live state, not a screenshot.
Create account
Form field + input + button
Active users
Counter + delta chip
+5.2K this week
Amount
Input group + prefix
Notifications
Switch stack
Push notifications
Real-time activity
Daily digest
8:00 AM summary
Beta features
Early access
Notification
Alert surface
Payment received
2 new comments
Empty state
Empty primitive
Team
Avatar stack + badge
Topics
Tag cluster
Tabbed view
Tabs + content
Standalone components, signals-first.
Page
Pagination
Showing page 2 of 8
Profile
Form field + hint
Theming
Segmented drives the page theme
Search
Input + kbd shortcut
Press ⌘K from anywhere on the docs.
Loading
Skeleton placeholders
Profile
Avatar + meta + tags
Roman Kim
Founding engineer
Install
Drop-in component
import { WrBtn } from 'ngwr/button';
@Component({
imports: [WrBtn],
template: `<wr-btn color="primary">Save</wr-btn>`,
})
export class Demo {}Each entry point is its own ng-packagr build.
ngwr is what we wish the Angular standard library was. Every primitive is a standalone import, every input is a signal, every visual is a CSS variable. No NgModules, no global registries, no magic strings.
anyimport { Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { WrBtn } from 'ngwr/button';
import { WrFormField } from 'ngwr/form';
import { WrInput } from 'ngwr/input';
@Component({
selector: 'signup-card',
imports: [FormsModule, WrFormField, WrInput, WrBtn],
template: `
<wr-form-field label="Work email" required>
<input wrInput type="email" [(ngModel)]="email" />
</wr-form-field>
<wr-btn color="primary" block (click)="reserve()">
Reserve your spot
</wr-btn>
`,
})
export class SignupCard {
protected readonly email = signal('');
protected reserve(): void {
// 1 input, 1 button, 1 signal. No NgModule, no ControlValueAccessor.
}
}Each primitive comes with the bookkeeping handled — focus traps, virtual scroll, controlled state, density tokens — so the day goes into what the user actually sees.
A11y in the lib
Focus traps, ARIA, keyboard nav, screen-reader labels — tested with axe + VoiceOver, not inferred from a doc page.
Every visual is a token
Every color, radius, spacing, and duration is a `--wr-*` variable. Re-skin one component or all of them.
Standalone, zoneless
Every component is a standalone import. Works the same in CLI, Vite, SSR with hydration, and zoneless apps.
Pay for what you import
Each component is its own ng-packagr entry. What you don't import never lands in your bundle.
Dark mode is first-class
`[data-theme]` flips at the root or any subtree. `prefers-color-scheme` is one provider option, not a hardcoded check.
Motion you can trust
One easing curve shared by overlays, hovers, presses, route changes. `prefers-reduced-motion` short-circuits the lot.
Beyond the primitives — a kit of motion components for hero copy, hovers, and the bits of personality that set a real product apart.
Shiny text
<wr-shiny-text />
Glitch text
<wr-glitch-text />
Rotating text
<wr-rotating-text />
Decrypt text
<wr-decrypt-text />
Blur text
<wr-blur-text />
Split text
<wr-split-text />
Typewriter
<wr-typewriter />
Gradient text
<wr-gradient-text />