angular-tailwind-components
v22.1.0
Published
Angular UI component library built with Tailwind CSS v4 — signals, standalone components, 54 components.
Maintainers
Readme
Angular Tailwind Components
A comprehensive Angular component library built entirely with Tailwind CSS v4 — zero additional UI dependencies.
Live Storybook: angular-tailwind-components.vercel.app
Features
- 🎨 54 components — signals, standalone, modern control flow
- 🎯 Pure Tailwind CSS v4 — no third-party UI framework
- 📝 ControlValueAccessor on every form component
- ♿ Accessible — WCAG 2.1 AA, ARIA APG patterns, full keyboard support
- 🎭 Themeable — semantic color tokens, radius roles, dark mode
- 🧪 Tested with Vitest, documented in Storybook
Compatibility
The library major matches the Angular major: library 22.x → Angular 22, 23.x → Angular 23, and so on.
| Library | Angular | Tailwind CSS | Notes | | :------- | :------ | :----------- | :----------- | | 21.x | 21 | 4 | Previous. | | 22.x | 22 | 4 | Current. |
Peer dependencies: @angular/core ^22, tailwindcss ^4, postcss ^8.
Installation
npm install angular-tailwind-componentsRegister the library stylesheet in angular.json (architect.build.options.styles) — it is what emits the semantic tokens and utilities (bg-primary-600, text-on-primary-*, …):
"styles": [
"node_modules/angular-tailwind-components/styles/tailwind.css",
"src/styles.css"
]That file already includes @import "tailwindcss", the library @theme block and the @source paths for classes used inside components. Using only @import "tailwindcss" in src/styles.css is not enough: without the library stylesheet the semantic tokens are missing and primary buttons render gray. Keep src/styles.css for app-specific rules only.
Quick Start
import { Component } from '@angular/core';
import { TailwindButtonModule, TailwindInputModule, TailwindToggleModule } from 'angular-tailwind-components';
@Component({
selector: 'app-example',
imports: [TailwindButtonModule, TailwindInputModule, TailwindToggleModule],
template: `
<form [formGroup]="form">
<tailwind-input label="Email" [formControl]="form.controls.email" />
<tailwind-toggle label="Notifications" [formControl]="form.controls.notifications" />
<tailwind-button color="primary" (click)="submit()">Submit</tailwind-button>
</form>
`
})
export class ExampleComponent {
form = new FormGroup({
email: new FormControl(''),
notifications: new FormControl(false)
});
}Every component ships an NgModule named after it: importing TailwindTableModule brings the table together with its row template and header directives, TailwindAccordionModule brings the accordion and its items. The individual classes stay exported, so imports: [TailwindButton] keeps working when you want a narrower import.
Configuration
provideTailwindConfig is the single entry point. It configures the injection tokens — ICON_SIZE, ICON_BASE_PATH, DATETIME_LANGUAGE, COMPONENTS_SIZE, BUTTON_KIND, PAGINATION_SUMMARY, PASSWORD_LABELS, EDITOR_LABELS, TITLE_SCALE, LABELS — and the two themes, RADIUS and COLORS.
import { ApplicationConfig } from '@angular/core';
import { provideTailwindConfig } from 'angular-tailwind-components';
export const appConfig: ApplicationConfig = {
providers: [
provideTailwindConfig({
COMPONENTS_SIZE: 'md',
BUTTON_KIND: 'solid',
DATETIME_LANGUAGE: 'it',
LABELS: { close: 'Chiudi', search: 'Cerca' },
RADIUS: 'round',
COLORS: { primary: 'indigo', neutral: 'zinc' }
})
]
};Token values resolve on first injection, after your app initializers. RADIUS and COLORS write CSS to the document at startup and are a no-op during SSR.
A factory is also accepted — use it only when the values need inject():
provideTailwindConfig(() => {
const t = inject(TranslocoService);
return { LABELS: { close: t.translate('common.close') } };
});A factory is read once for the tokens and once, during the initializers, for RADIUS / COLORS — so keep those two keys static rather than derived from async state.
Icons are loaded at runtime from /tailwind-icons/<name>.svg; when the app is not served from the domain root, point the library at the right directory with provideTailwindConfig({ ICON_BASE_PATH: '/my-app/tailwind-icons' }).
Every label in LABELS also has a matching component input (closeLabel, searchLabel, …) when a single instance needs a different string.
provideTailwindThemeColors and provideTailwindRadius still exist as standalone providers but are deprecated in favour of the COLORS and RADIUS keys.
Theming
Semantic colors
COLORS remaps primary, neutral, success, warning, danger (alias error) and info at runtime by injecting <style id="tailwind-theme-colors"> in @layer theme. Each key accepts:
| Form | Example | Effect |
| :-------------------- | :------------------------------------------------- | :---------------------------------------------------------------- |
| Tailwind palette name | primary: 'indigo' | Maps every shade to var(--color-indigo-<shade>) |
| Per-shade colors | success: { 600: '#14532d' } | Writes --color-success-600 |
| { shades, on } | success: { shades: {…}, on: { 600: '#ecfdf5' } } | Also writes --color-on-success-*, the foreground on that ground |
primary and neutral cover shades 50–950; the severity colors stop at 900. With a palette string the on-* defaults stay consistent on their own. A custom family name that Tailwind does not emit needs @source inline("bg-<name>-{50,{100..900..100},950}") in your stylesheet.
Radius is a role, not a size
Components name what they are — rounded-control (buttons, inputs, chips), rounded-surface (cards, tables), rounded-overlay (menus, popovers, modals) — so one key restyles the library:
provideTailwindConfig({ RADIUS: 'round' }); // 'sharp' | 'compact' | 'default' | 'round'
provideTailwindConfig({ RADIUS: { control: '0.375rem', overlay: '1rem' } });Surface tokens
Four fills, each with one job:
| Token | Used for |
| ---------------- | ---------------------------------------------------------------- |
| surface | The panel itself — card, table, menu, modal |
| surface-subtle | Chrome zones — card header/footer, table thead, editor toolbar |
| surface-muted | Interaction — row hover, disabled field, segmented-control track |
| surface-raised | Anything floating above the panel |
surface-subtle is derived (color-mix(in oklab, var(--color-fg) 3%, var(--color-surface))), so it
tracks light, dark and any brand palette on its own. Header and footer zones carry a fill or a
rule, never both — a card's header is separated by a hairline until headerBg / footerBg swaps it
for the tint.
Dark mode
Components paint themselves with surface tokens (bg-surface, text-fg, border-border), so dark mode is a variable remap. It is opt-in: put class="dark" on <html>, or class="theme-auto" to follow the OS. Dark rules are scoped to :root.dark and outrank a custom brand palette.
CSS override
Any token can still be overridden in your own stylesheet:
@theme {
--color-primary-600: var(--color-violet-600);
--color-ring: var(--color-primary-500); /* focus ring for every control */
--radius-control: 0.5rem;
}Sizing
Every control takes its height from one shared scale, so a button, an input and a date picker of the same size line up exactly.
| size | height | text | icon |
| ------ | ------------- | ----------- | ---- |
| xs | h-6 (24px) | text-xs | 16 |
| sm | h-8 (32px) | text-sm | 16 |
| md | h-9 (36px) | text-sm | 16 |
| lg | h-11 (44px) | text-base | 20 |
| xl | h-13 (52px) | text-base | 24 |
Conventions
- Consumer
class— every component acceptsclass/[class]and merges it onto the internal surface (the visible root element). The merge is conflict-aware: a class you pass replaces the base class of the same group instead of sitting beside it, sobg-red-50repaints a card,rounded-nonereshapes it andshadow-noneclears its elevation. Both are single-class selectors of equal specificity, so without this the winner would be whichever Tailwind emits last — alphabetical by value, which is whybg-red-500used to lose tobg-surfacewhilebg-teal-500won. Utilities that collide with nothing (h-full,mb-4) are appended as before. - Content slots — named slots use attribute selectors on native elements:
<div tailwind-card-header>…</div>,<div tailwind-modal-content>…</div>. - Pipes —
TailwindSafeHtmlPipe(safehtml) sanitizes HTML for[innerHTML]; used internally forerrorTextandhelperText. - Focus and motion — one focus color (
--color-ring), three durations, andprefers-reduced-motionrespected out of the box.
Components
Form controls (with ControlValueAccessor)
- Input (
tailwind-input) — text, email, password, number, search - Input Password (
tailwind-input-password) — strength meter and show/hide toggle - Textarea (
tailwind-textarea) — resize modes, rows/cols - Editor (
tailwind-editor) — WYSIWYG rich text, sanitized HTML value, link/image insertion - Upload (
tailwind-upload) — button or drop zone; base64 value for forms,filesSelectedfor raw files - Input OTP (
tailwind-input-otp) — multi-digit PIN with paste and keyboard navigation - Checkbox (
tailwind-checkbox) / Radio Group (tailwind-radio-group) / Toggle (tailwind-toggle) - Select (
tailwind-select) — combobox on the CDK overlay, optional multi-select with removable chips - Autocomplete (
tailwind-autocomplete) — typeahead with async search and#itemtemplate - Slider (
tailwind-slider) — single or range, optional ticks - Calendar Panel / Date Picker / Time Picker / DateTime Picker
- Segmented Control (
tailwind-segmented-control) — exclusive choices as an ARIA radio group - Number Input (
tailwind-number-input) — real increment/decrement buttons, clamped tomin/max - Rating (
tailwind-rating) — star rating exposed as a slider
Display
- Button (
tailwind-button) — six kinds × seven colors, icon-only, loading - Badge, Chip, Tag — semantic labels, removable chips for filters
- Card (
tailwind-card) — header/body/footer withcomfortableorcompactdensity - Avatar (
tailwind-avatar) — image, initials or icon fallback with status dot - Title (
tailwind-title) — semantich1–h6with optional icon - Kbd (
tailwind-kbd) — keys and chords as native<kbd> - Timeline (
tailwind-timeline,tailwind-timeline-item) — ordered events as an<ol> - Carousel (
tailwind-carousel,tailwind-carousel-slide) — autoplay pauses on hover and focus - Order List (
tailwind-order-list) — reorder by buttons, keyboard or drag, as a multi-select listbox
Feedback
- Alert (
tailwind-alert) — icon, title, dismiss, actions slot - Spinner, Progress Bar, Skeleton — loading indicators
- Empty State (
tailwind-empty-state) — icon, headline and call to action - Toast (
tailwind-toast-container) — global notifications throughTailwindToastService - Message (
tailwind-message) — form-level inline message
Navigation
- Tab Group (
tailwind-tab-group) — WAI-ARIA tabs with arrow-key support - Breadcrumb, Pagination, Menu, Stepper
- Tree (
tailwind-tree) — ARIA tree pattern with flattened rendering
Layout / overlay
- Modal (
tailwind-modal) — dialog, also openable throughTailwindModalService - Drawer (
tailwind-drawer) — slide-in panel from any edge - Accordion (
tailwind-accordion) — expandable sections - Tooltip, Popover, Popconfirm — anchored overlays with viewport flipping
- Table (
tailwind-table) — projected header/rows, per-column comparators, sticky header, select-all, client- or server-side sort and paging - Toolbar, Divider, Meter
License
Licensed under the Angular Tailwind Components License 1.0 (ATC-1.0) — see LICENSE.
- You may use the library in applications and sell those applications.
- You may not sell or redistribute the library itself as a standalone UI library product.
Bundled third-party assets keep their own licenses: the Heroicons outline icons are © Tailwind Labs, MIT.
