@konce-pt/angular
v0.11.0
Published
Open-source Angular 22 component library on Signals & Signal Forms — 70+ components, rich data table, zoneless, fully tokenized. Selektory kpt-*, klasy Kpt*.
Maintainers
Readme
Koncept UI — Angular
Open-source Angular 22 component library built on Signals & Signal Forms. 70+ components, a feature-rich data table and app-shell layout — zoneless, standalone, fully tokenized.
Why Koncept UI
Koncept UI is a free, MIT-licensed component library for Angular 22, built from the ground up on a modern signal-based foundation:
- ⚡ Signals everywhere —
input(),output(),model(),computed.OnPush+ zoneless by default. - 📝 Forms on Signal Forms only — components implement
FormValueControl<T>and bind through thefield/Controldirective. NoControlValueAccessor. - 📊 Flagship data table — sorting, global filter, pagination, virtual scroll (5000+ rows), row selection, column reorder & freeze, CSV export, cell templates.
- 🎨 100% tokenized — every style is a
var(--kpt-*)CSS custom property. Light/dark theme, neutral OKLCH palette. Swap the brand by swapping one token layer. - 🧩 Standalone components — no NgModules. Import only what you use; tree-shakeable.
- ♿ Built on Angular CDK — overlays, a11y, focus management, virtual scrolling.
- 🌍 i18n built in — English (default) & Polish out of the box, runtime locale switch, add any language with one JSON.
Installation
npm i @konce-pt/angular @konce-pt/tokens @konce-pt/styles @angular/cdk
# or: pnpm add / yarn add@angular/core, @angular/common and @angular/forms (all ^22) are already in an Angular app; @angular/cdk (^22) is a required peer — overlay components (select, autocomplete, datepicker, date-range, dialog, drawer, tooltip, popover, menu…) build on the CDK Overlay — so install it explicitly (above). Optional: @konce-pt/validators for the Polish-market validators (NIP, REGON, PESEL, IBAN, postal code). Optional: @konce-pt/grid for the mobile-first layout system — its Angular directives live in @konce-pt/angular/grid.
Setup
Angular CLI (recommended)
Add the design tokens, base styles and — if you use overlay components — the CDK overlay CSS to the styles array in angular.json. Order matters: tokens first, your own src/styles.scss last so your overrides win the cascade.
// angular.json → projects.<app>.architect.build.options
"styles": [
"node_modules/@konce-pt/tokens/dist/css/tokens.css",
"node_modules/@konce-pt/tokens/dist/css/tokens.dark.css",
"node_modules/@konce-pt/styles/index.css",
"node_modules/@angular/cdk/overlay-prebuilt.css",
"src/styles.scss"
]Zoneless bootstrap (Angular 22) lives in your app config — no CSS imports in main.ts:
import { provideZonelessChangeDetection } from '@angular/core';
// import { provideKptI18n } from '@konce-pt/angular';
// import { provideKptTablerIcons } from '@konce-pt/angular/icons';
providers: [
provideZonelessChangeDetection(), // required (zoneless)
// provideKptI18n({ locale: 'pl' }), // optional — UI is English by default; set for Polish/other
// provideKptTablerIcons(), // optional — full Tabler icon set
]The UI language defaults to English and works with zero config — provideKptI18n(...) is optional and only needed for Polish (or a custom language). See Internationalization below.
Dark theme: set <html data-theme="dark"> (or rely on prefers-color-scheme).
Why not
import '…css'inmain.ts? Angular CLI (@angular/build) turns side-effect CSS imports into a separate stylesheet that isn't linked fromindex.html, so the app builds without styles. Useangular.json → styles[]instead.
Other bundlers (Vite / webpack)
Non-Angular-CLI bundlers can import the CSS directly (this is how the Koncept UI playground works):
import '@konce-pt/tokens/css';
import '@konce-pt/tokens/css/dark';
import '@konce-pt/styles';
import '@angular/cdk/overlay-prebuilt.css';Quick start
A login form powered by Signal Forms — no ControlValueAccessor, no FormsModule:
import { Component, computed, signal } from '@angular/core';
import { FormField, FormRoot, email, form, required, submit } from '@angular/forms/signals';
import { KptButton, KptFormField, KptInput } from '@konce-pt/angular';
@Component({
selector: 'app-login',
imports: [FormRoot, FormField, KptFormField, KptInput, KptButton],
template: `
<!-- formRoot sets novalidate, intercepts submit and calls submit() for you -->
<form [formRoot]="loginForm" (submit)="onSubmit()">
<kpt-form-field label="E-mail" [error]="emailError()" required controlId="f-email">
<kpt-input [formField]="loginForm.email" type="email" id="f-email" />
</kpt-form-field>
<kpt-form-field label="Password" [error]="passwordError()" required controlId="f-pass">
<kpt-input [formField]="loginForm.password" type="password" id="f-pass" />
</kpt-form-field>
<kpt-button type="submit" [disabled]="formInvalid()">Sign in</kpt-button>
</form>
`,
})
export class Login {
readonly model = signal({ email: '', password: '' });
readonly loginForm = form(this.model, (p) => {
required(p.email, { message: 'E-mail is required' });
email(p.email, { message: 'Enter a valid e-mail address' });
required(p.password, { message: 'Password is required' });
});
// Validators stay pure — only the *message* waits for the field to be touched.
private errorFor(name: 'email' | 'password'): string | null {
const s = this.loginForm[name]();
return s.touched() && s.invalid() ? s.errors()[0]?.message ?? null : null;
}
readonly emailError = computed(() => this.errorFor('email'));
readonly passwordError = computed(() => this.errorFor('password'));
readonly formInvalid = computed(() => this.loginForm().invalid());
async onSubmit(): Promise<void> {
// submit() marks the whole tree as touched before running the action
await submit(this.loginForm, { action: async () => { console.log(this.model()); } });
}
}Don't gate validators on UI state (
when: ({ state }) => state.touched()) — it makesloginForm().valid()returntrueon an untouched form. Koncept UI controls already show the invalid state only once a field is touched.
Data table with sorting, filtering, pagination and a custom cell template:
<kpt-data-table [columns]="columns" [data]="users()" filterable exportable [pageSize]="10"
selectable="multiple" rowKey="id" [(selection)]="selected">
<ng-template kptCell="status" let-value="value">
<kpt-badge [value]="value" />
</ng-template>
</kpt-data-table>Components (70+)
| Category | Components | | --- | --- | | Forms (Signal Forms) | input, textarea, select, autocomplete, checkbox, switch, radio-group, slider, rating, datepicker, date-range, color-picker, file-upload, input-number, password, input-otp, chips-input, input-mask, listbox, knob, rich-text | | Buttons & actions | button, icon-button, button-group, fab, split-button, speed-dial | | Layout | app-shell, toolbar, sidenav, card, divider, panel, fieldset, splitter, scroll-top | | Navigation | tabs, accordion, breadcrumb, stepper, menu, menubar, megamenu, context-menu | | Data | data-table, paginator, tree, timeline, carousel, data-view, pick-list, order-list, galleria, meter-group | | Feedback & overlay | alert, dialog, toast, tooltip, popover, drawer, bottom-sheet, confirm, badge, chip, avatar, avatar-group, spinner, progress, skeleton, empty, image |
Every component ships an llms.txt API sheet next to its source for quick reference.
Theming
All visuals are driven by var(--kpt-*) tokens (three tiers: primitives → semantic → component). Override them in your src/styles.scss (it's last in styles[], so it wins the cascade):
:root {
--kpt-color-primary: oklch(0.55 0.2 265); /* rebrand in one line */
--kpt-color-primary-hover: oklch(0.5 0.2 265);
}
:root[data-theme='dark'] {
--kpt-color-primary: oklch(0.7 0.16 265);
}Common token names: surfaces --kpt-color-surface, --kpt-color-surface-sunken|raised|variant|hover|selected; text --kpt-color-on-surface, --kpt-color-on-surface-muted (aliases --kpt-color-text, --kpt-color-text-muted|subtle|inverse); borders --kpt-color-border, --kpt-color-border-strong; accent roles in full — {primary,danger,success,warning,info} each with -hover, -contrast, -subtle, -border; radii --kpt-radius-sm|md|lg|xl|full|none; elevation --kpt-elevation-1..4 (aliases --kpt-shadow-sm|md|lg). There is no --kpt-color-bg.
Icons
84 icons are built in and need no configuration — what the components themselves draw plus the
staples of an application shell (sun, moon, bell, settings, users, logout,
layout-dashboard, home, activity, chart-bar, brand-github …). A typical admin panel needs
nothing else.
For the full Tabler Icons set (MIT, 5130 icons) there are two paths, and the difference is the bundle. Every icon is its own export, so importing by name lets the bundler keep just those:
// Production — two icons reach the bundle.
import { LayoutDashboard, Rocket } from '@konce-pt/angular/icons';
import { provideKptIcons } from '@konce-pt/angular';
// providers: [provideKptIcons([LayoutDashboard, Rocket])]
// Prototype — every icon, about 1.2 MB.
import { provideKptTablerIcons } from '@konce-pt/angular/icons';
// providers: [provideKptTablerIcons()]provideKptIcons comes from the core entry point on purpose: @konce-pt/angular/icons imports
the whole set statically, so a subset parameter there could not shrink anything. For the same
reason provideKptIcons(tabler) with a namespace import (import * as tabler) registers
everything — handing a namespace object to a function blocks static analysis.
Internationalization (i18n)
Component labels ship in English (default) and Polish. The KptI18n service is providedIn: 'root', so English works out of the box with no provider. Call provideKptI18n(...) only to start in Polish (or another language), or to register/override dictionaries. All user-facing strings — aria-labels, empty states, paginator, calendars, rich-text menus — read from this signal-based service, so switching locale updates the UI instantly (zoneless-friendly).
import { provideKptI18n } from '@konce-pt/angular';
// providers: [provideKptI18n({ locale: 'pl' })] // optional — Polish UI (EN is the default)Switch at runtime:
import { inject } from '@angular/core';
import { KptI18n } from '@konce-pt/angular';
const i18n = inject(KptI18n);
i18n.setLocale('en');Add any language with one JSON (shape = the KptMessages contract; missing keys fall back to English):
import de from './i18n/de.json';
provideKptI18n({ locale: 'de', messages: { de } });Override individual labels of an existing language:
provideKptI18n({ messages: { en: { paginator: { rowsPerPage: 'Rows per page:' } } } });Calendar weekday/month names come from the browser's Intl API for the active locale. Per-component label inputs (e.g. emptyMessage, acceptLabel) still take precedence over the dictionary.
Documentation
- Storybook — interactive docs for every component.
- Playground — live demos with copyable HTML/TS/SCSS.
llms.txt— LLM-friendly API sheets per component, shipped inside the package; the project-wide sheet lives at ui.konce.pt/llms.txt.- Security scanners — Socket.dev and similar tools flag i18n keys as domain names (
.email,.todayand friends are real TLDs) and the map's default endpoints; the project sheet explains each alert under "Security scanners". - Repository: gitlab.com/konce-pt/koncept-ui
License
MIT © konce.pt
🇵🇱 Wersja polska
Koncept UI to darmowa biblioteka komponentów na licencji MIT dla Angulara 22, zbudowana od podstaw na nowoczesnym fundamencie sygnałów.
- ⚡ Sygnały wszędzie —
input(),output(),model(),computed.OnPush+ zoneless domyślnie. - 📝 Formularze wyłącznie na Signal Forms — komponenty implementują
FormValueControl<T>, bezControlValueAccessor. - 📊 Flagowa tabela danych — sortowanie, filtr, paginacja, virtual scroll, zaznaczanie, przestawianie/zamrażanie kolumn, eksport CSV, szablony komórek.
- 🎨 100% na tokenach — każdy styl to
var(--kpt-*). Motyw jasny/ciemny, neutralna paleta OKLCH. - 🧩 Standalone — bez NgModules, tree-shaking.
- 🌍 Wbudowane i18n — angielski (domyślny) i polski, przełączanie języka w runtime, dowolny język jednym plikiem JSON.
Instalacja
npm i @konce-pt/angular @konce-pt/tokens @konce-pt/styles @angular/cdk@angular/core, @angular/common i @angular/forms (^22) są już w aplikacji Angulara; @angular/cdk (^22) to wymagany peer — komponenty overlay (select, autocomplete, datepicker, date-range, dialog, drawer, tooltip, popover, menu…) bazują na CDK Overlay — więc instaluj go jawnie (powyżej). Opcjonalnie: @konce-pt/validators (walidatory PL: NIP, REGON, PESEL, IBAN, kod pocztowy).
Konfiguracja
Angular CLI (zalecane) — dodaj CSS do tablicy styles w angular.json; kolejność ważna, src/styles.scss na końcu:
"styles": [
"node_modules/@konce-pt/tokens/dist/css/tokens.css",
"node_modules/@konce-pt/tokens/dist/css/tokens.dark.css",
"node_modules/@konce-pt/styles/index.css",
"node_modules/@angular/cdk/overlay-prebuilt.css",
"src/styles.scss"
]W Angular CLI nie używaj
import '…css'wmain.ts— bundler wrzuca taki CSS do osobnego, niepodlinkowanego arkusza i aplikacja jest bez stylów. Dla Vite/webpack import wmain.tsdziała (tak robi playground).
Zoneless bootstrap (Angular 22) w konfiguracji aplikacji — bez importów CSS w main.ts:
import { provideZonelessChangeDetection } from '@angular/core';
// import { provideKptI18n } from '@konce-pt/angular';
// import { provideKptTablerIcons } from '@konce-pt/angular/icons';
providers: [
provideZonelessChangeDetection(), // wymagane (zoneless)
// provideKptI18n({ locale: 'pl' }), // opcjonalne — UI domyślnie po angielsku; ustaw dla PL/innego
// provideKptTablerIcons(), // opcjonalne — pełny zestaw Tabler
]Język UI to domyślnie angielski i działa bez żadnej konfiguracji — provideKptI18n(...) jest opcjonalny, potrzebny tylko dla polskiego (lub własnego języka); szczegóły w sekcji i18n niżej.
Motyw ciemny: <html data-theme="dark">; nadpisania --kpt-color-* w styles.scss. Pełny zestaw ikon Tabler: provideKptTablerIcons() z @konce-pt/angular/icons.
Układ strony: @konce-pt/grid (osobna paczka CSS) plus dyrektywy [kptGrid], [kptCol], [kptFlex] z @konce-pt/angular/grid — siatka mobile-first, utilities flexbox i opcjonalne container queries.
Internacjonalizacja (i18n)
Etykiety komponentów są dostępne po angielsku (domyślnie) i polsku. Serwis KptI18n jest providedIn: 'root', więc angielski działa od razu, bez providera. provideKptI18n(...) wołasz tylko, by wystartować po polsku (lub w innym języku) albo zarejestrować/nadpisać słowniki. Wszystkie napisy (aria-label, stany puste, paginator, kalendarze, menu edytora) pochodzą z tego serwisu opartego o sygnały — zmiana języka odświeża UI natychmiast (zoneless).
import { provideKptI18n } from '@konce-pt/angular';
// providers: [provideKptI18n({ locale: 'pl' })] // opcjonalne — polski interfejs (EN domyślnie)Przełączanie w runtime: inject(KptI18n).setLocale('en').
Własny język jednym plikiem JSON (kształt = kontrakt KptMessages; brakujące klucze spadają na EN):
import de from './i18n/de.json';
provideKptI18n({ locale: 'de', messages: { de } });Nadpisanie pojedynczych etykiet: provideKptI18n({ messages: { pl: { paginator: { rowsPerPage: 'Na stronie:' } } } }).
Nazwy dni/miesięcy w kalendarzach pochodzą z Intl wg locale. Inputy etykiet per-komponent (np. emptyMessage) mają priorytet nad słownikiem.
Komponenty (70+)
Formularze (Signal Forms), przyciski i akcje, layout, nawigacja, dane (z flagową kpt-data-table), feedback i overlay — pełna lista w tabeli powyżej. Każdy komponent ma plik llms.txt z opisem API obok źródła.
Dokumentacja
Storybook (interaktywne docs), playground (dema z kopiowaniem kodu), llms.txt (opisy API — obok każdego komponentu w paczce, całość projektu pod ui.konce.pt/llms.txt). Repozytorium: gitlab.com/konce-pt/koncept-ui.
Alerty skanerów łańcucha dostaw (Socket.dev i podobne) — klucze i18n czytane jak domeny oraz domyślne endpointy mapy — omawia sekcja „Skanery bezpieczeństwa" w arkuszu projektu.
Licencja
MIT © konce.pt
