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

@temirtator/kazmaps-design-system

v2.1.0

Published

KazMaps design system — one UI kit for every KazMaps brand (Figma tokens, components, icons) for Tailwind v4 + React 19 apps

Readme

@temirtator/kazmaps-design-system

Дизайн-система KazMaps: один UI-кит для всех продуктов — KazMaps, KazMaps Business, KazMaps Booking и KazMaps Developer Hub. Компоненты сняты с макета Figma «KazMaps Design System» пиксель в пиксель и сверяются с ним автоматически (npm run test:figma); бренд меняет только значения токенов, не компоненты. React 19, Tailwind CSS v4, TypeScript.

Установка

npm i @temirtator/kazmaps-design-system lucide-react

Peer-зависимости:

  • react ^19.0.0
  • react-dom ^19.0.0
  • lucide-react ^1.0.0
  • qrcode ^1.5.4 — необязательный, нужен только QrCode

Подключение в Next.js или Tailwind v4 приложение

1. Стили в globals.css

@import "tailwindcss";
@import "@temirtator/kazmaps-design-system/styles/core.css";
@import "@temirtator/kazmaps-design-system/styles/theme.css";
@import "@temirtator/kazmaps-design-system/styles/brands/maps.css";
@import "@temirtator/kazmaps-design-system/styles/kit.css";

@source "../../node_modules/@temirtator/kazmaps-design-system/dist";

Файл бренда — один из brands/maps.css, brands/business.css, brands/booking.css, brands/devhub.css. Порядок менять нельзя: --ease-standard объявлен и в core.css, и в файле бренда с одной специфичностью — побеждает тот, что подключён позже, то есть бренд.

theme.css даёт утилиты Tailwind вида bg-background-primary, text-text-secondary, border-border-primary для всех цветовых ролей; можно писать и напрямую — bg-(--background-primary).

kit.css — утилиты и keyframes, которые читают компоненты (transition-interactive, focus-ring, gradient-ring, animate-modal-in, animate-shimmer-placeholder).

2. Атрибуты на корневом элементе

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html data-brand="maps" data-theme="light" lang="ru">
      <body>{children}</body>
    </html>
  );
}
  • data-brand: maps | business | booking | devhub.
  • data-theme: light | dark. Без атрибута maps показывает светлую тему и следует prefers-color-scheme; business и booking — тёмную; devhub — светлую и системную тему не подхватывает, data-theme ставит сам.

Оба атрибута ставятся на <html>: theme.css объявляет --color-* на :root, и при атрибуте на другом элементе утилиты bg-background-primary перестанут находить значения.

3. Шрифт

Бренд maps использует переменный Inter с весами 400, 450, 500 и 550 через --font-inter. В Next.js — next/font без списка weight, иначе 450 и 550 не загрузятся:

import { Inter } from "next/font/google";

const inter = Inter({ subsets: ["latin", "cyrillic"], variable: "--font-inter" });

Бренды business, booking и devhub держат готовый литеральный стек в --font-sans (Inter, Lexend, Golos Text соответственно) — специальной настройки загрузчика, как у --font-inter для maps, им не нужно, шрифт достаточно подключить в приложении.

4. Флаги стран

PhoneInput показывает флаг региона эмодзи — так он нарисован в макете. macOS, iOS и Android рисуют флаги сами; Windows вместо флага показывает две буквы. Для Windows подключите в приложении шрифт с флагами один раз при старте:

npm i country-flag-emoji-polyfill
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";

polyfillCountryFlagEmojis(); // регистрирует шрифт «Twemoji Country Flags» только там, где флагов нет

Компонент ставит этот шрифт первым в стеке, поэтому больше ничего настраивать не нужно.

5. Компоненты

import { Button, TextInput, PlaceRow, useToast } from "@temirtator/kazmaps-design-system";

Вход /maps оставлен как реэкспорт корня и будет удалён; новые импорты — из корня.

6. Иконки

Набор иконок макета: 104 заливных глифа в поле 24×24, три раздела — Interface, Map UI, Weather.

import { IconSearchBold, IconStarLight } from "@temirtator/kazmaps-design-system/icons";

<IconSearchBold size={20} />
<IconStarLight title="Рейтинг" />

Одноцветные иконки залиты currentColor. Компоненты кита рисуют глифы из этого же набора. Иконки погоды многоцветные: читают роли --weather-* и на currentColor не реагируют. Без title иконка декоративная и скрыта от скринридера. ICON_MANIFEST перечисляет набор, раздел и начертание каждой иконки.

Типографика

Heading/Text/Caption в 1.0.0 удалены — вместо компонентов кит-класс kit.css даёт утилиты для пар кегль/интерлиньяж и весов, встречающихся в макете: type-10-12, type-12-16, type-14-18, type-14-20, type-16-20, type-20-24, type-32-40 и weight-400, weight-450, weight-500, weight-550.

<p className="type-16-20 weight-500 text-(color:--text-primary)">Профиль</p>

Компоненты

Действия: Button (accent | neutral | danger), IconButton (Map Action, 36), IconButtonGroup (зум), Chip (neutral | info | success | warning | danger — три последних построены на ролях tag-*, в макете не нарисованы), Toggle, PillTabs, MenuTabs, NavItem, Avatar.

Поля: TextInput, PasswordInput, SearchInput (обводка, иконка и ввод; отправку вешает потребитель — onKeyDown у поля или <form> вокруг него), PhoneInput, CodeInput, SelectField, DayPicker, Textarea, Checkbox, FieldMessage (подпись под полем через aria-describedby; три последних в макете не нарисованы — docs/figma-deltas.md).

Списки: Pagination (номера страниц, окно с многоточиями; в макете не нарисован).

Оверлеи: Dialog, NonmodalDialog, BottomSheet, ToastProvider + useToast, Menu + MenuItem + MenuDivider.

Карта: MapCompass (клик возвращает на север; с heading поворотом управляет приложение), MapTrafficBadge, WeatherBadge, ForecastCard, ScaleBar, NavBar, CollapseHandle.

Содержимое: PlaceRow, ListRow, SectionHeader, EmptyState, ShimmerBlock, StarRating, LogoLockup, LegalLink, InDevelopment, QrCode.

Хелперы: cn, useFocusTrap, isTopmostTrap, REGIONS, findRegion, parseE164, formatE164, toE164, isKazakhstanMobile.

Ошибки — только тостом

Ни одно поле не рисует текст ошибки под собой: поля принимают булев invalid, а текст показывает useToast. Так во всех продуктах KazMaps.

const show = useToast();
<TextInput value={v} onChange={setV} invalid={!valid} />;
if (!valid) show("Введите номер полностью");

PhoneInput

<PhoneInput label="Номер телефона" onChange={({ e164, complete }) => complete && send(e164)} />

| Проп | Тип | Описание | | ----------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------- | | value | string | E.164 ("+77012345678") или "" — контролируемый режим. | | defaultValue | string | Начальное E.164 для неконтролируемого режима. | | defaultRegion | RegionCode | Регион, когда value/defaultValue пусты; по умолчанию "KZ". | | onChange | (value: PhoneValue) => void | На каждое изменение; value.e164 пуст, пока номер не введён полностью. | | onRegionChange | (region: RegionCode) => void | Только при смене региона — выбор в пикере или вставка номера с другим кодом страны. | | onFocus, onBlur | FocusEventHandler | Проксируются на внутренний <input>. | | regions | RegionCode[] | Ограничивает список регионов. | | locale | "ru" \| "en" | Язык названий стран; по умолчанию "ru". | | label | string | Подпись над полем; пустая строка или пробелы — без подписи. Подсказок и текста ошибки под полем нет. | | invalid | boolean | Красная обводка и aria-invalid; текст ошибки — тостом. | | required, disabled, readOnly | boolean | | | id, name, autoFocus, autoComplete | | Как у обычного <input>; autoComplete по умолчанию "tel-national". | | labels | Partial<PhoneInputLabels> | Тексты пикера: имя кнопки региона, плейсхолдер поиска, заголовки групп, пустой результат. | | className | string | Класс корневой обёртки. | | ref | Ref<HTMLInputElement> | Ссылка на внутренний <input>: ref={field.ref} у react-hook-form фокусирует поле с ошибкой. | | aria-describedby | string | Связывает поле с текстом ошибки, выведенным рядом с ним. | | aria-label | string | Имя поля без видимой подписи; при непустом label не применяется. | | aria-labelledby | string | Применяется всегда. Видимая подпись имеет id <id>-label — сошлитесь на неё, чтобы собрать составное имя. |

  • Селектор показывает флаг, код страны набран текстом в поле: «+7 701 234 56 78». Пикер — все страны с поиском, точные маски для СНГ, остальным цифры группами и проверка длины E.164. Вставка +998… переключает регион сама; 8 701… нормализуется в KZ.
  • Код +1 общий для нескольких стран; parseE164 и вставленный +1… по умолчанию дают US.
  • React Hook Form — через Controller: onChange={(v) => field.onChange(v.e164)} и ref={field.ref}, чтобы форма могла поставить фокус в поле с ошибкой.
  • Контролируемый value="" не очищает частично введённое поле: компонент пересинхронизируется только когда внешний value отличается от последнего отправленного E.164 — иначе поле дёргалось бы во время набора.
  • locale переключает только названия стран; остальные тексты пикера — из labels, дефолты русские.

Токены

Контракт один на все бренды: имена ролей — имена Figma Variables (группа/роль → --группа-роль).

  • Цвета по темам: action/*, background/*, overlay/*, border/*, text/*, icon/*, tag/*, traffic/*, weather/*, map/*; effect/shimmer-peak.
  • Статические: dimension/corner-radius/*, dimension/height|width/*, spacing/gap|padding/*, stroke/*, shadow/blur|position/*, typography/font-size|line-height/*, font-sans, font-weight-*, text-xs…3xl, тени и тайминги кита (shadow-hud, motion-panel, ease-standard и другие).

Источник истины — tokens/schema.json и tokens/brands/*.json; CSS и docs/tokens.md генерирует npm run tokens:build. Бренд maps импортируется из tokens/figma/export.json командой npm run tokens:import; business, booking и devhub пока переходные (_pending в файле бренда) — docs/brands.md. Чего не хватает в макете — docs/figma-deltas.md. Какой компонент макета каким стал в коде — docs/figma-map.md. Как кит сверяется с макетом — docs/figma-compare.md; там же таблица «Пропуски с дельтами» — всё, что осталось несверенным.

Обводки повторяют strokeAlign макета и места не занимают: внутренние рисуются inset-ring, градиентное кольцо Button — отдельным слоем gradient-ring, внешние — ring. Высоты совпадают с макетом: Chip и SelectField 28, IconButton 36, TextInput, PhoneInput и SearchInput 36, Button 40, ячейка CodeInput 48, PlaceRow 72. У Avatar и MapCompass коробка 36, а белый круг 40 нарисован внутри и вылезает по 2 px — так объявлено в макете.

Переопределение токенов в приложении

Селектор должен быть не слабее селектора бренда [data-brand="…"] (0,1,0), иначе значения бренда победят:

:root[data-brand] {
  --font-sans: var(--font-inter, "Inter"), system-ui, sans-serif;
}

--font-inter должен быть объявлен загрузчиком шрифтов — голый var() на несуществующую переменную инвалидирует весь --font-sans.

Разработка

Storybook

У каждого компонента две истории: All Variants — все состояния макета статикой, и Playground — один экземпляр с контролами. Переключатели бренда × темы в тулбаре действуют на любую историю. По умолчанию канва — фон страницы Figma (#a7a7a7 + белый 25 %), чтобы сравнивать компонент с макетом один в один; переключатель Canvas возвращает фон бренда:

npm run storybook

Опубликованная версия: https://temirtator.github.io/kazmaps-design-system/

Проверки

npm run lint
npm run typecheck
npm run test          # tokens:check + check:tokens + vitest
npm run test:vrt      # скриншоты Storybook, Playwright
npm run test:e2e      # взаимодействия PhoneInput
npm run test:figma    # сверка историй с макетом по снапшоту figma/ds
npm run build

Иконки из Figma

Нужен персональный токен Figma с областью file_read в переменной FIGMA_TOKEN или в локальном .env:

npm run icons:import
npm run icons:build

Импорт заменяет src/icons/svg только после того, как все глифы скачаны. Слои с подчёркиванием в начале имени в набор не входят. npm run icons:check падает, если сгенерированные компоненты разошлись с исходными SVG.

Снапшот и сверка с Figma

Бинарь пакета kazmaps-figma умеет четыре команды: snapshot (снять файл Figma в репозиторий), expect (напечатать ожидания сверки по ноде снимка), codemod-tokens и check-tokens (миграция клиента на контракт токенов — два раздела ниже). Те же функции доступны экспортом @temirtator/kazmaps-design-system/figma-tools.

figma/ds/ — закоммиченный снимок файла «KazMaps Design System»: tree.json (обрезанное дерево нод), manifest.json (каждый вариант компонента: id, свойства, размер, рендеры), renders/** (PNG в 1×). Обновляется отдельным PR «design bump»:

zsh -ic 'npm run figma:snapshot'   # FIGMA_TOKEN из профиля шелла

Тот же снапшот доступен как бинарь пакета — npx kazmaps-figma snapshot --file=ds — и как экспорт @temirtator/kazmaps-design-system/figma-tools (snapshot, figmaFetch, trimNode, slug, variantSlug, expectationsFor, codemodTokens, checkTokens) для скриптов вне репозитория. Прогон делает 6 запросов к API (файл + 5 батчей рендеров) и затем 231 последовательную загрузку PNG с CDN с 5-секундной паузой между батчами — в сумме около минуты.

Ревью PR «design bump» смотрит на manifest.json: sha256 меняется только у тех рендеров, что правда поменялись в Figma, так что диф по манифесту и есть список изменений — сами PNG открывать не нужно.

Сверка кода с макетом — npm run test:figma (vrt/figma.spec.ts, карта vrt/figma-map.ts): для каждой записи карты (нода Figma ↔ история Storybook ↔ селектор) слой 1 сверяет числа — размеры, паддинги, gap, радиус, заливку, боксы именованных слоёв (data-figma="<имя слоя>") и типографику текстов; слой 2 гоняет pixelmatch рендера истории против PNG из снапшота и считает кластеры отличий вне текста; слой 3 — тест покрытия, который падает, пока в карте не хватает варианта из manifest.json (иконки не в счёт, их держит icons:check). Отчёт с тремя картинками на вариант (макет, код, диф) — test-results/figma-report/index.html. Правила замера (a–g) и таблица «Пропуски с дельтами» — всё, что осталось несверенным и почему — в docs/figma-compare.md.

Что слой 1 ждёт от конкретной ноды, печатает expect — с этого удобно начинать новую запись карты:

npx kazmaps-figma expect --node=147:264 --layers="Button Text"   # Button, State=Default/Accent

Codemod токенов у клиентов

Клиенты (business-client, booking-client, developer-hub-client) ещё держат имена токенов 0.x — --ink, --muted, --line, --brand, --card, --bg, --radius-*, --danger… Таблица переименований — scripts/figma/token-map.json: any — замены, одинаковые для всех брендов, byBrand — радиусы (у каждого бренда своя шкала отступов), manual — токены без механической замены, потому что старое имя расходится на несколько ролей кита в зависимости от контекста (например --danger — это то --text-danger, то --action-danger-primary, то --tag-red).

npx kazmaps-figma codemod-tokens --root=src --brand=business   # отчёт, файлы не тронуты
npx kazmaps-figma codemod-tokens --root=src --brand=business --write

--brand — один из business, booking, devhub, maps; неизвестный бренд — usage и код возврата 1. У devhub --radius-lg — ближайшее совпадение шкалы (14px между 12 и 16), а не точное значение, — решение дизайнера открыто в дельте №31 (docs/figma-deltas.md). Без --write печатается только JSON-отчёт: changed — какие файлы и сколько замен из any/byBrand было бы сделано, manual — где в коде встретились токены без автозамены (файл, строка, токен, подсказка какую роль кита выбрать вместо него) — эти места правит руками тот, кто делает миграцию. Функция доступна и как экспорт — codemodTokens из @temirtator/kazmaps-design-system/figma-tools.

check-tokens: гейт на старые токены и сырой hex

Гейт ищет по --root (src/**/*.{ts,tsx,css,mdx} по умолчанию) старые имена токенов — все ключи token-map.json (any, byBrand, manual) — и сырые hex-цвета (#fff, #ffffff, #ffffff80…), которые должны были стать переменными кита.

npx kazmaps-figma check-tokens --root=src --allow=hex-allow.json

Без находок — выход 0 и пустой вывод. При находках печатает по строке file:line kind match (kind — token или hex) и завершается кодом 1.

Формат allow-файла (шаблон — scripts/figma/hex-allow.example.json):

{
  "files": ["e2e/**", "**/*.stories.tsx", "**/*.test.tsx", "**/*.test.ts"],
  "patterns": ["--blob-", "--strip-"]
}

files — глобы (**/*) по путям относительно --root, файл при совпадении пропускается целиком (истории, тесты, e2e-фикстуры). patterns — подстроки: находка на строке, которая их содержит, не считается (свои переменные лендинга вроде --blob-opacity/--strip-color, которые не входят в контракт кита и намеренно не токенизируются).

Сам DS гоняет гейт на себе — npm run check:tokens (входит в npm test), конфиг — scripts/figma/hex-allow.ds.json:

  • **/*.stories.tsx, **/*.test.tsx, **/*.test.ts — примеры и фикстуры, а не код кита;
  • styles/** — сгенерированные палитры брендов (src/styles/brands/*.css) — это и есть определения hex внутри токенов;
  • docs/** — истории Storybook внутри документации;
  • components/qr-code.tsx — qrcode кодирует изображение по буквальным #000000/#ffffff, контраст QR обязан оставаться максимальным независимо от бренда и темы — токенизировать здесь нечего;
  • паттерн #0f1214 — буква в Avatar без снимка держит фиксированные тёмные чернила поверх любого цвета tag/* (дельта №14, docs/figma-deltas.md), а не токен кита.

Функция доступна и как экспорт — checkTokens из @temirtator/kazmaps-design-system/figma-tools.

Релизы

Версию поднимает релизный PR, тег и публикация — за владельцем.

  1. Ветка chore/release-X.Y.Z: секция ## [X.Y.Z] — YYYY-MM-DD в CHANGELOG.md, правки документации и версия "X.Y.Z" руками в package.json и package-lock.json (без npm version). Один коммит chore(release): X.Y.Z, PR с тем же заголовком.
  2. Полные гейты перед ревью — весь блок «Проверки» выше плюс npm pack --dry-run: в списке пакета должны быть scripts/figma/*.mjs и все четыре dist/styles/brands/*.css, а __tests__ — нет.
  3. После мержа — владелец:
git checkout main && git pull
git tag vX.Y.Z && git push --tags
npm whoami && npm owner ls @temirtator/kazmaps-design-system
npm publish   # OTP

1.0.0 — один кит на все бренды; business-client и booking-client переезжают с 0.3.0 по docs/brands.md.

Лицензия

MIT, copyright Temirlan Shagyrov, 2026.