npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rt-tools/ui-kit-v2

v0.39.0

Published

Набор компонентов Angular и дизайн-система к ним: шкалы оформления, светлая и тёмная темы. Компоненты сигнальные и `OnPush`, разметка размечена BEM-директивами из `@rt-tools/core`, каждый интерактивный элемент несёт `qa-dataid`.

Readme

@rt-tools/ui-kit-v2

Набор компонентов Angular и дизайн-система к ним: шкалы оформления, светлая и тёмная темы. Компоненты сигнальные и OnPush, разметка размечена BEM-директивами из @rt-tools/core, каждый интерактивный элемент несёт qa-dataid.

Пакет живёт рядом с @rt-tools/ui-kit, а не вместо него: селекторы (rt-* против rtui-*), свойства оформления и наборы компонентов у них не пересекаются, поэтому одно приложение может держать оба.

Установка

pnpm add @rt-tools/ui-kit-v2 @rt-tools/core @rt-tools/utils

quill нужен только тем, кто ставит редактор текста. Он и два компонента на нём — поле ввода сообщения и чат — стоят отдельным входом @rt-tools/ui-kit-v2/rich-editor:

import { RtChatComponent, RtMessageComposerComponent, RtRichEditorComponent } from '@rt-tools/ui-kit-v2/rich-editor';

Основной вход quill не называет, и приложение без этих трёх компонентов собирается без него.

pdfjs-dist нужен только тем, кто открывает просмотрщик документа PDF. Он стоит отдельным входом @rt-tools/ui-kit-v2/pdf-viewer, а файл воркера pdf.worker.min.mjs приложение кладёт себе в assets/pdfjs/ при сборке — подробности на странице обзора rt-pdf-viewer:

import { RtPdfViewerComponent } from '@rt-tools/ui-kit-v2/pdf-viewer';

Основной вход pdfjs-dist не называет, и приложение без просмотрщика собирается без него.

Точки входа компонентов

Каждый компонент собран своей точкой входа по имени своего каталога: @rt-tools/ui-kit-v2/table, @rt-tools/ui-kit-v2/calendar и так далее. Настройки, словари, среда исполнения, токены и чистые функции лежат во входе @rt-tools/ui-kit-v2/core. Корень пакета переотдаёт их все, и прежние импорты из @rt-tools/ui-kit-v2 работают как раньше. В кусок бандла сборщик кладёт только модули тех компонентов, что импортированы.

Компонент внутри @defer берётся из своей точки входа. Отложенный блок грузит импорт целиком, и импорт из корня оставил бы в бандле весь пакет. Типы идут отдельной строкой import type: импорт, где рядом с компонентом стоит тип, Angular не откладывает.

import { RtTableComponent } from '@rt-tools/ui-kit-v2/table';
import type { IRtStatTile } from '@rt-tools/ui-kit-v2/stat-tile';

Подключение

import { provideRtIDBStorage } from '@rt-tools/core';
import { provideRtIcons } from '@rt-tools/ui-kit-v2';

export const appConfig: ApplicationConfig = {
    providers: [provideHttpClient(), provideRtIcons(), provideRtIDBStorage()],
};

provideRtIDBStorage() нужен rt-data-table, rt-data-list и rt-table: они хранят настройку колонок в базе браузера. Без него страница с ними пустая, а в консоли стоит NG0201: No provider found for _IDBStorageService.

Оформление — одной строкой в точке входа приложения; в пакете лежит и собранный CSS со всеми свойствами, и исходники партиалов:

@use '@rt-tools/ui-kit-v2/styles/tokens.css';

Стили кита объявлены слоем каскада rt-kit — тремя подслоями в порядке vendor, base, components. Правило приложения, объявленное вне слоя, выигрывает у любого правила кита независимо от специфичности: перебирать её счётом и обходить чужую вёрстку больше не нужно.

Объявления свойств на корне страницы в слой не уходят — их приложение перебивает порядком, объявляя своё после tokens.css.

Тёмная тема кита включается только признаком на корне: [data-theme='dark'] или класс html.rt-theme-dark. Системную настройку CSS кита не читает: по ней признак ставит ThemeService в режиме auto. Своя тема Material или класс тёмной темы приложения кит не переключают. На узле ниже корня [data-theme='dark'] даёт тёмный кусок страницы.

Под материальным набором тёмная тема не перебивает цвета, которые набор берёт из ролей темы приложения --rt-sys-*. Поиск, поля отбора и пагинация следуют теме приложения, а она отвечает тёмной сама. Без темы Material на странице набор рисует тёмные цвета кита.

Оба признака на корне ставят на body фон-градиент тёмной темы. Приложению со своим фоном страницы его нужно перебить своим правилом body.

Приложение, которое переезжает с @rt-tools/ui-kit и пока держит оба кита, подключает ещё один файл — файл совместимости, он лежит рядом с tokens.css:

@use '@rt-tools/ui-kit-v2/styles/tokens.coexist.css';

Первый кит объявляет на корне страницы десять ступеней с теми же именами — --rt-radius-* и --rt-shadow-sm/md/lg — и своими значениями. Его файл подключается позже, и внутри компонентов второго кита действуют его ступени: скругление поля отбора выходит 8px вместо 4px. Файл совместимости ставит значения второго кита на узел материального набора (.rt-preset-material, [data-preset='material']), и такой узел рисуется своей шкалой, что бы ни лежало на корне. Набор может стоять и на самом корне страницы: селектор файла весит на ступень больше корня первого кита, и порядок подключения ничего не решает.

С этим файлом приложению не нужны свои скругления поверх кита: поле отбора, поле поиска и кнопки страниц берут значения второго кита сами.

Файл удаляется вместе с первым китом. Он повторяет значения шкалы второго кита, поэтому без первого кита вид от его удаления не меняется.

Подслой vendor — для чужого CSS, который чинит кит. Тема редактора текста подключается в него, иначе она выигрывает у правил кита и панель редактора перестраивается:

@import 'quill/dist/quill.snow.css' layer(rt-kit.vendor);

Сброс приложения вида * { margin: 0; padding: 0; } вне слоя выигрывает у кита по тому же правилу. Он обнуляет поля компонентов: ячейки таблицы, пункты меню и кнопки теряют отступы. Сброс кладётся в тот же подслой vendor. Подслой нижний, и кит перебивает сброс своими правилами, а остальная вёрстка приложения получает его как прежде:

@layer rt-kit.vendor {
    * {
        margin: 0;
        padding: 0;
    }
}

Полосу прокрутки кит рисует только своим узлам — с классом на rt-. Узлы приложения он не трогает, и их ::-webkit-scrollbar работает и в Chrome. Своя полоса приложения, поставленная всем узлам, кладётся в тот же подслой vendor: вне слоя она перебивает полосы кита, и таблица перестаёт прятать полосу, которую человек выключил в настройке колонок.

Иконки лежат отдельными файлами в node_modules/@rt-tools/ui-kit-v2/src/assets/icons: сборка копирует каталог в свою статику, а provideRtIcons('/icons') называет адрес, по которому он опубликован. Файл забирается тогда, когда значок с этим именем попросила разметка, и дальше живёт в inline-sprite: страница платит за те значки, которые нарисовала, а не за весь набор.

{
    "glob": "**/*.svg",
    "input": "node_modules/@rt-tools/ui-kit-v2/src/assets/icons",
    "output": "icons"
}

Приложение, которое объявляет материальный набор оформления, публикует и второй набор значков — рисунки Material из src/assets/icons-material. Его адрес по умолчанию /icons-material, свой называется вторым аргументом: provideRtIcons('/icons', '/icons-material'). Без этой записи кит рисует свои значки и под материальным набором: пустого места не будет, но и рисунка Material тоже.

{
    "glob": "**/*.svg",
    "input": "node_modules/@rt-tools/ui-kit-v2/src/assets/icons-material",
    "output": "icons-material"
}

Приложение, пришедшее с первого кита, называет значки именами Material — их принимает вход glyph у rt-icon, а у кнопки rtButton тот же вход icon. Имя с парой в перечне кита рисуется этой парой, без пары — лигатурой шрифта Material Symbols. Этот шрифт кит не везёт: приложение подключает его само, семья переназначается свойством --rt-icon-glyph-font. Третий аргумент выбирает способ: provideRtIcons('/icons', '/icons-material', { glyphStrategy: 'font' }) рисует шрифтом любое имя, по умолчанию стоит map-first.

Шрифт кит везёт сам. Начертания Montserrat лежат в node_modules/@rt-tools/ui-kit-v2/src/assets/fonts, объявления @font-face — в слое оформления, и адрес в них относительный: сборка приложения разрешает его от места файла стилей и уносит файлы в свою статику сама. Отдельной настройки это не просит — в отличие от значков, которые публикуются по адресу.

Начертание переменное, одно на все насыщенности: кит объявляет веса 400, 500, 600 и 700 и берёт их из одной оси. Подмножеств четыре — кириллица, кириллическое расширение, латиница и латинское расширение, каждое в прямом и наклонном начертании; браузер качает только те, чьи знаки на странице встретились. Всего 332 КБ, русской странице достаются два файла из восьми.

Письменностей, которых в Montserrat нет, кит не везёт: тайская, хангыль и деванагари добавляются приложением — своим @font-face и своим именем в конце --rt-font-family-sans. Именовать семейство, которого кит не везёт, он не будет: названное и не привезённое рисуется тем, что случайно стоит на машине читателя, а отступы и высоты строк кита рассчитаны под Montserrat.

Что внутри

Компоненты — 73 семейства: поля формы (rt-input, rt-select, rt-multiselect, rt-date-picker, rt-date-range, rt-file-input, rt-rich-editor, rt-field), таблица с настройкой колонок и пагинацией, панель правки записи с маршрутом и защитой несохранённого, диалоги, чат с композером, календарь, шапка страницы с навигацией, тосты, просмотрщик фотографий, показ размеченного текста (rt-markdown-text) и остальное.

Дизайн-система — src/styles:

  • tokens.source.mjs — вход источника: ступени шкал, назначения и ответ тёмной темы рядом со своим назначением. Сами узлы лежат частями рядом — tokens.scale-*.mjs для шкал, tokens.light-*.mjs для назначений, tokens.dark-layout.mjs и tokens.coarse-pointer.mjs для порядка тёмного файла и грубого указателя, — а вход складывает их в два ряда. Три файла ниже и имена в src/lib/tokens/rt-design-tokens.ts собираются отсюда командой pnpm run build:tokens-source и руками не правятся;
  • _primitives.scss — шкалы: нейтральные и цветные ряды, отступы, радиусы, тени, типографика;
  • _semantic.scss — назначения поверх шкал (--rt-color-bg-surface, --rt-color-text-muted);
  • _coarse-pointer.scss — поправка назначений для сенсорного экрана: поле ввода не мельче 16px;
  • _theme-dark.scss — переопределения назначений для [data-theme='dark'];
  • _theme-dark-rules.scss — правила тёмной темы, которые свойств не объявляют: подложка экранов входа и инверсия логотипа;
  • _theme-scope.scss — те же назначения для узла, а не для корня: под ними живёт метка rtTheme, и каждое правило объявляет весь набор целиком;
  • _scrollbar.scss — оформление полос прокрутки;
  • _breakpoints.scss — SCSS-переменные порогов: свойства оформления в @media не работают, поэтому пороги остаются переменными препроцессора;
  • _mixins.scss — миксины поверхности карточки и её заголовка;
  • _login.scss — раскладка страницы входа.

_coexist.scss — ступени второго кита на узле материального набора на время переезда с первого кита. Его собирает тот же генератор из источника шкалы, а файл совместимости собран из него отдельно.

Всё, кроме _breakpoints.scss и _coexist.scss, собирает _index.scss — из него и собран tokens.css. Пороги и миксины подключаются партиалами напрямую:

@use 'pkg:@rt-tools/ui-kit-v2/styles/breakpoints' as bp;
@use 'pkg:@rt-tools/ui-kit-v2/styles/mixins';

Среда исполнения — ThemeService (три состояния темы, признак на <html data-theme>, переживает перезагрузку), RtThemeScopeDirective (метка rtTheme: своя тема у узла), BreakpointsService (сигналы совпадения по ширине), NotificationBus (шина тостов).

Умолчания кита — provideRtKit: приложение одной раздачей задаёт тему старта, вид кнопки, шторы, таблицы, списка, полей выбора и поведение подсказки. Раздача необязательна, а вход, написанный в разметке, её перебивает.

Подписи — 131 ключ: кнопки диалогов, тексты ошибок поля, расшифровки иконок для скринридера. Своего языка у кита нет и быть не может — его знает приложение.

Без единой настройки кит рисует английский набор, вшитый умолчанием: пустая aria-подпись означала бы кнопку без имени для скринридера, а это хуже чужого языка. Свои подписи приложение отдаёт функцией-переводчиком:

import { provideRtKitLabels, TRtKitLabelKey, TRtKitLabelParams } from '@rt-tools/ui-kit-v2';

provideRtKitLabels({
    translator: computed(() => (key: TRtKitLabelKey, params?: TRtKitLabelParams) => myDictionary(key, params)),
    locale: myLocale, // Signal<string>; ею кит форматирует даты
});

Способ доставки — дело приложения: Transloco, $localize, собственный словарь. Кит знает только ключи (TRtKitLabelKey — перечень выводится из английского набора, поэтому опечатка не доживает до рантайма) и то, что на них отвечают готовой строкой: подстановки вида {{name}} переводчик делает сам. Подпись, которой он не дал, берётся английской — поштучно, не набором.

Скругление одним входом

Каждый компонент с поверхностью принимает вход radius — шаг общей шкалы: none, xs, sm, ms, md, lg, xl, 2xl, full. Без входа компонент скруглён по макету, и умолчание у каждого своё.

<rt-card radius="none">…</rt-card>
<rt-input radius="full" />
<button rtButton label="Сохранить" radius="sm"></button>

Шаг ложится на хост атрибутом data-rt-radius и переназначает своё свойство главной поверхности. Вложенный компонент его не наследует: скруглённая карточка не скругляет кнопки внутри. Внутренние части — точка, значок, кольцо фокуса — держат своё скругление. Круглую кнопку для всего приложения задаёт настройка кита components.button.radius.

Входа нет у подтверждения, всплывающего уведомления, подсказки и просмотра фото: их рисует директива или сервис поверх страницы, и тега для шага у приложения нет. Все шаги всех компонентов показаны на странице витрины Foundation / Radius.

Настройка компонента снаружи

Компонент не берёт ступень шкалы там, где рисует себя: между ступенью и местом употребления стоит своё свойство компонента — --rt-<блок>-<что>. Объявлено оно в корне блока, значением по умолчанию стоит та самая ступень, поэтому без единой строки от приложения кит выглядит так же, как выглядел.

Своими свойствами покрыты четыре рода назначений:

| Род | Имя свойства | Где применяется | | ------------- | ----------------------------------- | --------------------------------------------------------------- | | скругление | --rt-<блок>-*-radius | рамка блока, пузырь, значок, плашка, кольцо аватара | | тень | --rt-<блок>-*-shadow | карточка, панель в перекрытии, кольцо фокуса, приподнятая точка | | толщина рамки | --rt-<блок>-*-border-width | обводка, разделитель строк, дорожка, полоса под вкладкой | | длительность | --rt-<блок>-*-transition-duration | переход цвета, фона, размера и сдвига |

Отступ, кегль и габариты остаются на ступенях намеренно: роль отступа одним словом не называется, и свойство под неё вышло бы именем места, а не именем роли.

Приложение переопределяет свойство правилом той же цели и большей силы — наследования тут мало, объявление живёт на самом блоке:

.my-app .rt-button {
    --rt-btn-radius: 0;
}

Часть, которую браузер уносит из поддерева блока — панель списка, меню, перетаскиваемая строка, — объявляет свойство на себе, и целиться в неё надо тоже отдельно:

.my-app .rt-select__panel {
    --rt-select-panel-shadow: none;
}

Что ни один компонент этот порядок не обошёл, проверяет pnpm run check:tokens-styles: ступень, взятая в стилях компонента напрямую, роняет прогон. Показ на витрине — страница Foundation / Design Tokens / Component Props.

Свой логотип

rt-logo рисует то, что даст приложение: своих начертаний кит не везёт.

:root {
    --rt-logo-wordmark: url('/logo/wordmark.svg');
    --rt-logo-tagline: url('/logo/tagline.svg');
    --rt-logo-wordmark-dark: url('/logo/wordmark-inverse.svg');
    --rt-logo-tagline-dark: url('/logo/tagline-inverse.svg');
}

Пропорции своего файла компоненту сообщает вход aspect — без него ширина считается по умолчанию варианта и узкое начертание обрастёт пустотой.

Разработка

pnpm run build:ui-kit-v2          # сборка пакета и CSS с токенами
pnpm exec nx test @rt-tools/ui-kit-v2
pnpm exec nx lint @rt-tools/ui-kit-v2
pnpm exec stylelint "projects/ui-kit-v2/**/*.scss"

Переход на слой каскада

Стили кита переехали в слой rt-kit, и это меняет вид у тех, кто настраивал кит поверх. Ошибки сборки не будет — узнаётся глазами, поэтому названо здесь прямо.

  • Правило приложения теперь выигрывает у правила кита независимо от специфичности. Место, где кит перебивали поднятой специфичностью или обходом чужой вёрстки, продолжит работать, но надобности в этом больше нет: обычное правило сильнее любого правила из слоя.
  • Правило кита теперь проигрывает и чужой библиотеке, чей CSS объявлен вне слоя. Тема редактора текста подключается в подслой vendor — импортом с именем подслоя; подключённая мимо, она перестраивает панель редактора.
  • Кит больше не настаивает на своём восклицательным знаком. Таблица в карточном виде перестала перебивать min-width приложения: приложению, которому нужен и горизонтальный скролл, и карточный вид, остаётся сузить свой селектор до широкого экрана.