- Проживание, 4 ночи
- 114 000 ₸
- Поздний выезд
- 9 600 ₸
- Оплачено с сайта
- 57 000 ₸
- К оплате
- 66 600 ₸
UI kit · v1.0 · 30.10.2026
Every control,
every state
The components every Nomero app shares: the PMS, ID, Chat, the console and hotel sites. One stylesheet, ui.css, draws all of them. Apps use these class names and never restyle a component locally.
- Day and Night
- Works with reka-ui and Radix
- Plain HTML, Vue, React
Rules every component follows
These rules keep five apps built by different people looking like one product.
Three sizes, one grid. Small is 32px (tables, toolbars, filters), medium is 40px (the default), large is 48px (hotel sites, phones, the one main action). A button and an input of the same size always line up.
Variants are classes, states are attributes. Write
nm-btn nm-btn-ghost nm-btn-sm. Never add a class for a state; setdisabled,aria-invalid,aria-busy,aria-current, or let reka-ui and Radix setdata-state. The look follows the accessibility state, so the two cannot disagree.Every control has all six states. Hover, pressed, focus, disabled, busy and invalid. Focus is always a cobalt ring drawn 2px off the edge.
Inputs use 16px text. Smaller text makes phones zoom on focus. Only the small size, used in dense tables and toolbars, drops to 14px.
No spinners. Waiting is shown with the window loader, a pen stroke under a busy button, or hatching shaped like what is coming.
Drawn, not decorated. Hairlines instead of shadows. Anything a draftsman would letter (table heads, title blocks, breadcrumbs, captions) is mono uppercase. Every number is set in Geist Mono.
One primary action per view. The cobalt pill appears once. Everything else is an outline or a ghost.
Missing something? Add it to
ui/nomero-ui.css, rebuild, and it reaches every app. Never fork a component inside an app.
Drawing marks
The pieces that make a screen read as a sheet from a drawing set rather than a generic dashboard. Use them for structure and labels, never as ornament.
Гостиница Алатау · Брони
Заезд · 30.10 · 14:00- Отель
- Алатау
- Лист
- Этаж 4
- Масштаб
- 1:100
- Занято
- этаж 9 из 13 · отель 36 из 41
Loaders and progress
No spinners. Short waits light the mark's windows in the order you would write the n. Known lengths show a bar. Content shows its own hatched skeleton.
Skeletons
Shaped like the content that is coming, so nothing jumps when it arrives. The hatching drifts like a drawing still being made, and moves with transform only.
Text fields
Label above, hint or error below, nothing inside the box but the value. Focus draws a cobalt ring. An error turns the border red and says what to fix.
Checkboxes, radios and switches
Checkboxes pick items, radios pick one option, switches change a setting that applies at once. The label is always clickable.
Selects, comboboxes and chips
A select for a short fixed list, a combobox when people type to find, a segmented control for two to four views, chips for filters.
Dates, counts, codes and files
The inputs a hotel needs every day. Dates always read as a stay: arrival, departure and the number of nights.
Menus, dialogs and drawers
Things that float get the raised surface and the overlay shadow. A dialog asks one question; a drawer shows a record without leaving the page; a sheet is the phone version of both.
Отменить бронь #4821?
Айгерим Жумабаева, 29 окт → 2 ноя. Гостю придёт сообщение об отмене.
Alerts, banners and toasts
Alerts sit next to what they are about and stay until fixed. Banners cover the whole app. Toasts confirm what just happened and leave on their own after five seconds.
Последняя отправка в 01:05, ошибок нет.
298 броней перенесены, расхождений нет.
Напомните им оплатить до 14:00, иначе номера вернутся в продажу.
Тариф «Стандарт без завтрака» не связан с тарифом канала. Свяжите их в настройках каналов.
Badges, counts, tags and avatars
Status is a legend mark and a word, never color alone. A cobalt count means guests are waiting; an outlined count is just a number.
Tables, cards and stats
Tables carry the work. Numbers align right in tabular figures, names come with initials, the sorted column is marked and a selected row is lit.
| Гость | Номер | Канал | Статус | Действия | |||
|---|---|---|---|---|---|---|---|
АЖАйгерим Жумабаева+7 701 234 56 78 | 402 | 29 окт → 2 ноя | WSСайт | В номере | 114 000 ₸ | ||
СБСауле БековаЗаезд сегодня в 14:00 | 403 | 30 окт → 1 ноя | BKBooking.com | Подтверждено | 73 800 ₸ | ||
ДСДанияр Сериков+7 702 655 13 80 | 401 | 31 окт → 3 ноя | PHТелефон | Не оплачено | 85 500 ₸ | ||
МОМадина Оспанова2 взрослых, 1 ребёнок | 404 | 1 ноя → 5 ноя | ABAirbnb | Частично оплачено | 147 600 ₸ | ||
ЕАЕрлан АбеновОтменил 28 окт | 403 | 2 ноя → 4 ноя | WAWhatsApp | Отменено |
Бронь #4821
В номере- Гость
- Айгерим Жумабаева
- Номер
- 402
- Заезд
- 29.10
- Выезд
- 02.11
- Ночей
- 4
- Сумма
- 114 000 ₸
- Оплачено
- 57 000 ₸
Делюкс с видом на горы
28 м² · двуспальная кровать · до 3 гостей- Гостья заселилась в номер 40229 окт, 14:12 · Динара, ресепшен
- Поздний выезд до 15:00 добавлен30 окт, 10:05 · Ерлан, ресепшен
- Предоплата 57 000 ₸ получена12 окт, 22:44 · Kaspi
- Списание с карты отклонено12 окт, 22:42 · банк гостя
- Бронь создана с сайта отеля12 окт, 22:41
Chat
Guest messages on the left on paper with their channel in mono, staff replies on the right in cobalt, system notes in the middle in mono.
Empty and error states
No blank panels. An empty view says why it is empty and offers the next step. An error page says what broke and what to do, and the mark's unlit windows stand in for the missing content.
Сегодня заездов нет
Следующий заезд 31 октября: Данияр Сериков, номер 401.
Никого не нашли
По запросу «Ержан Ахметов» гостей нет. Проверьте написание или добавьте гостя.
Добавьте первый номер
Номера появятся в шахматке, и их можно будет продавать на сайте и в каналах.
Чатов пока нет
Подключите WhatsApp отеля, и сообщения гостей появятся здесь вместе с их бронями.
Error pages
Нет света в этом окне
Такой страницы нет. Возможно, ссылка устарела или в ней опечатка.
На главную- Лист
- 404
- Статус
- Не найден
Сервер не отвечает
Мы уже знаем о проблеме. Ваши данные в безопасности, обновите страницу через минуту.
- Лист
- 500
- Время
- 30.10 01:12
Нет доступа к отелю «Алатау»
Вы вошли как dinara@alatau.kz. Попросите администратора отеля добавить вас в сотрудники.
Нет соединения
Шахматка показывает данные на 01:05. Новые брони появятся, когда интернет вернётся.
Page layout
Every screen opens with the same header: where you are, what this is, and the one main action on the right. Forms group fields under short headings; settings put the switch on the right.
Брони
298 броней в октябре, 12 на сегодня
Using it in code
Import the stylesheet once. Wrap each component in the app's framework: behavior comes from reka-ui or Radix, the look comes from these classes.
@import "../brand/dist/fonts.css"; @import "../brand/dist/tokens.css"; @import "../brand/dist/ui.css"; /* Tailwind apps also: */ @import "../brand/dist/tailwind.css";
<SelectTrigger class="nm-input">
<SelectValue placeholder="Тип номера" />
<PhCaretUpDown class="nm-icon" />
</SelectTrigger>
<SelectContent class="nm-menu">
<SelectItem class="nm-menu-item" value="std">
Стандарт
</SelectItem>
</SelectContent><DropdownMenu.Content className="nm-menu">
<DropdownMenu.Item className="nm-menu-item">
<PencilSimple className="nm-icon" /> Изменить
</DropdownMenu.Item>
<DropdownMenu.Item className="nm-menu-item nm-menu-item-danger">
Удалить
</DropdownMenu.Item>
</DropdownMenu.Content><!-- NmButton.vue -->
<script setup lang="ts">
defineProps<{
variant?: 'primary' | 'secondary' | 'ghost'
| 'danger' | 'danger-soft'
size?: 'sm' | 'md' | 'lg'
busy?: boolean
}>()
</script>
<template>
<button :class="['nm-btn',
variant && variant !== 'primary' && `nm-btn-${variant}`,
size && size !== 'md' && `nm-btn-${size}`]"
:aria-busy="busy || undefined">
<slot />
<span v-if="busy" class="nm-dots" aria-hidden="true"><i/><i/><i/></span>
</button>
</template>reka-ui and Radix set the state.
data-state="open",data-highlighted,data-disabledandaria-selectedare already styled, so wrappers pass only the class.pms-front uses Headless UI. Give its components the same classes and set
aria-selectedordata-highlightedfrom its slot props.Tailwind stays for layout. Use utilities for gaps, grids and widths around components, never for a component's own colors, radius or height.