@alwayseducation/ui-kit
v1.1.2
Published
Reusable Angular 22 UI components for AlwaysConnected applications. The v2 library is standalone, signal-first, form-compatible, responsive, and styled with CSS tokens. It does not require Tailwind at runtime.
Readme
@alwayseducation/ui-kit
Reusable Angular 22 UI components for AlwaysConnected applications. The v2 library is standalone, signal-first, form-compatible, responsive, and styled with CSS tokens. It does not require Tailwind at runtime.
Installation
npm install @alwayseducation/ui-kitThe package declares Angular common, core, and forms as peer dependencies. CDK, Material utilities, TipTap, and XLSX are packaged runtime dependencies, so consumers do not need to install a second UI foundation or editor package.
Application setup
Import components directly in the standalone component that uses them:
import { Component } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { AcInput, AcSelect, AcSelectOption, AcTextarea } from "@alwayseducation/ui-kit";
@Component({
selector: "app-profile-form",
standalone: true,
imports: [FormsModule, AcInput, AcSelect, AcTextarea],
template: `
<ac-input label="Work email" type="email" placeholder="[email protected]" helperText="Use your company email." [(ngModel)]="email" />
<ac-select label="Role" [options]="roles" [searchable]="true" [(ngModel)]="role" />
<ac-textarea label="Bio" [rows]="4" [(ngModel)]="bio" />
`,
})
export class ProfileForm {
email = "";
role: string | null = null;
bio = "";
roles: AcSelectOption<string>[] = [
{ value: "designer", label: "Product designer", supportingText: "Design team" },
{ value: "engineer", label: "Engineer", supportingText: "Platform team" },
];
}For reactive forms, replace [(ngModel)] with [formControl] or formControlName and import ReactiveFormsModule. All v2 form controls implement ControlValueAccessor; Angular Forms remains responsible for validation, touched state, and disabled state.
Root theming
The default font is:
Inter, ui-sans-serif, system-ui, -apple-system, "system-ui", "Segoe UI", sans-serifSet the palette once in the consuming application's global styles.scss or styles.css:
:root {
--ac-theme-color: #7f56d9;
--ac-theme-hover: #6941c6;
--ac-theme-border-focus: #7f56d9;
--ac-theme-shadow-focus: 0 0 0 3px rgb(127 86 217 / 18%);
--ac-theme-bg-subtle: rgb(127 86 217 / 8%);
--ac-theme-border-subtle: rgb(127 86 217 / 25%);
--ac-theme-contrast-text: #fff;
}Optional color inputs inherit the root theme when omitted. Passing [color]="'#0ea5e9'", [color]="'emerald'", or another supported value generates a palette scoped to that component. AcThemeDirective can scope the same palette to a wrapper:
<section acTheme acColor="#0ea5e9">
<ac-input label="Themed field" />
<ac-button color="primary">Save</ac-button>
</section>Use var(--ac-...) tokens in new component styles. Keep components open to root overrides; do not inject a default host palette when the consumer has not supplied color.
Component inventory
Form controls
AcInput: text, email, phone, password, number, URL, search, icons, clear action, helper/error text, password visibility, and a floating label.AcTextarea: rows, maxlength, resize mode, helper/error text, CVA integration, and a floating label.AcSelect<T>: typed options, search, keyboard navigation, disabled options, empty state, CDK overlay, checkmark selection, and a floating label.AcMultiSelect<T>: typedT[]values, search, selected count, select-all, reset, checkbox indicators, footer, and empty state.AcAutocomplete: retained compatibility component for autosuggest workflows.AcDropdown: retained compatibility component for legacy dropdown integrations.
Example option model:
readonly options: AcSelectOption<string>[] = [
{ value: 'frontend', label: 'Frontend', supportingText: 'Web platform' },
{ value: 'backend', label: 'Backend', disabled: true },
];Use options on v2 selects. Do not use the old items, valueKey, displayKey, or multiple contract with AcSelect; use AcMultiSelect for multiple values.
Buttons and selection controls
AcButton, AcButtonGroup, AcButtonGroupItem, AcCheckbox, AcRadioGroup, AcRadioButton, AcToggle, and AcDatePicker are standalone and form-compatible.
<ac-button iconLeading="plus" (clicked)="createProject()">Create project</ac-button>
<ac-button-group [(ngModel)]="view" ariaLabel="View mode">
<ac-button-group-item value="day">Day</ac-button-group-item>
<ac-button-group-item value="week">Week</ac-button-group-item>
<ac-button-group-item value="month">Month</ac-button-group-item>
</ac-button-group>
<ac-checkbox label="Product updates" [(ngModel)]="productUpdates" />
<ac-radio-group label="Billing cycle" [(ngModel)]="billingCycle">
<ac-radio-button value="monthly" label="Monthly" />
<ac-radio-button value="annual" label="Annual" />
</ac-radio-group>
<ac-toggle label="Email notifications" [(ngModel)]="notificationsEnabled" />
<ac-date-picker label="Project due date" [(ngModel)]="dueDate" />The button group is responsive and uses a single outer border with internal separators. Set [themeColor] or its [color] alias on AcButtonGroup; set [themeColor] on AcButton because its color input is the semantic button variant. Place it inside a wrapper that can shrink; do not force a viewport width.
Text editor
AcTextEditor uses TipTap internally and supports:
- Selection-scoped inline formatting (font family, font size, color, bold, italic, underline).
- Rich links (prompts for URLs and supports both highlighted text and collapsed cursor text insertion).
- Image handling via URL prompt, local file upload picker, drag-and-drop, and clipboard paste.
- Editable tables with headers and data cells (
insertTable(rows, cols)). - HTML or JSON values, inline/floating toolbars, the library's
AcSelectfor font and size controls, alignment, lists, undo/redo, placeholder, read-only mode, and character counts.
<ac-text-editor label="Release notes" format="html" toolbar="inline" [characterLimit]="2000" [(ngModel)]="releaseNotes" />The editor is destroyed with the component lifecycle. Do not add a second editor package or replace its dropdowns with native <select> elements.
Messaging
Use AcMessageThread for the complete conversation or compose AcMessage, AcMessageComposer, and AcMessageStatus separately.
<ac-message-thread [messages]="messages" currentUserId="current-user" [composerActions]="[{ id: 'code', label: 'Code', icon: 'code' }, { id: 'schedule', label: 'Schedule', icon: 'calendar' }]" (send)="sendMessage($event)" (retry)="retryMessage($event)" (composerActionClick)="runAction($event)" />AcMessageComposer supports:
- 68+ categorized emojis with hover micro-interactions in a responsive grid.
- Browser-native microphone recording with real-time Speech-to-Text (STT) live streaming into
draft. - Multiple file and image previews with removal.
- Custom developer actions via
[actions](AcMessageComposerAction[]) with(actionClick)events. - Shortcuts, clean compact proportioned sizing, and
Enter/Shift+Entersubmit behavior.
Its voiceRecording output emits AcMessageVoiceRecording with a Blob, File, MIME type, and duration; maxVoiceDurationMs defaults to two minutes. Recording uses navigator.mediaDevices.getUserMedia and MediaRecorder; microphone access requires https or localhost plus user permission. AcMessageData supports text, replies, attachments, images, audio, link previews, reactions, and sending/sent/read/failed statuses.
Typed headless table
AcTable<T> is a reusable table primitive, not a Material dashboard. It supports sorting, single/multiple selection, indeterminate select-all, typed row actions, loading/empty states, responsive scrolling, compact/regular sizes, client/server pagination, and cell templates.
readonly columns: AcTableColumn<User>[] = [
{ id: 'name', header: 'Name', sortable: true, accessor: row => row.name },
{ id: 'email', header: 'Email', accessor: row => row.email },
];
readonly userKey = (row: User) => row.id;<ac-table [rows]="users" [columns]="columns" [rowKey]="userKey" selectionMode="multiple" [pagination]="{ pageIndex: 0, pageSize: 25, total: users.length, mode: 'client' }" (sortChange)="sort = $event" (selectionChange)="selected = $event" (paginationChange)="page = $event">
<ng-template acTableCell="email" let-value="value">{{ value }}</ng-template>
</ac-table>Filtering, virtualization, column resizing, and column reordering remain consumer-owned concerns.
Retained utilities
AcChip, Tag, AcSpinner, AcProgressBar, and AcCSVGrid remain exported for existing applications. Their accent states consume the shared palette; Tag and AcCSVGrid also accept an optional color input.
Accessibility and responsive rules
- Keep labels, hints, errors, required state, disabled state, and ARIA relationships connected through the component inputs.
- Preserve keyboard operation: Tab, Arrow keys, Enter/Space, Escape, and native form semantics.
- Components use
min-width: 0andmax-width: 100%; consumers should place them in a responsive wrapper and avoid fixed viewport sizing. - Do not render untrusted SVG or icon markup with
[innerHTML]. Use the typedAcIconNameregistry. - Prefer the built-in overlays and typed templates over Material form controls or ad-hoc dropdown markup.
Development and contribution
npm install
npm run build:all
npm test -- --watch=false --browsers=ChromeHeadless
./node_modules/.bin/tsc --project projects/ui-harness/tsconfig.app.json --noEmit
./node_modules/.bin/tsc --project projects/alwaysconnected-ui/tsconfig.spec.json --noEmit
git diff --checkWhen adding a component, keep it standalone and OnPush compatible, use input/output/signals, export it from src/public-api.ts, add unit tests, and add a live example to projects/ui-harness. The full integration rules are in skills/alwaysconnected-ui-kit/SKILL.md.
