Скилл переносится из ~/.claude/skills/aura-design в репозиторий обвязки: SKILL.md, справочники reference/ и набор kit/ (токены, base.css, компоненты React, оболочка AppShell, тест токенов, test-setup, примеры входа). install.sh ставит его в ~/.claude/skills/aura-design, --check его видит. tests/test-aura-design.sh проверяет структуру и инварианты набора. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
271 lines
20 KiB
CSS
271 lines
20 KiB
CSS
/* базовые компоненты AURA: только переменные из styles/tokens.css, веса 400/600/700 */
|
||
.ui-btn, .ui-icon-btn, .ui-seg__item, .ui-theme, .ui-chip__x {
|
||
font-family: var(--font-ui); cursor: pointer;
|
||
transition: background var(--motion), border-color var(--motion), color var(--motion);
|
||
}
|
||
.ui-btn:focus-visible, .ui-icon-btn:focus-visible, .ui-seg__item:focus-visible, .ui-theme:focus-visible, .ui-chip__x:focus-visible {
|
||
outline: 2px solid var(--color-focus-ring); outline-offset: 2px;
|
||
}
|
||
.ui-btn {
|
||
display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
|
||
height: var(--control-h); padding: 0 var(--space-4); border: 1px solid transparent; border-radius: var(--radius-round);
|
||
font-size: var(--fs-body); line-height: var(--lh-body); font-weight: 600; white-space: nowrap;
|
||
}
|
||
.ui-btn--lg { height: var(--control-h-lg); padding: 0 var(--space-5); }
|
||
.ui-btn--primary { background: var(--color-action-primary); color: var(--color-text-on-primary); }
|
||
.ui-btn--primary:hover:not(:disabled) { background: var(--color-action-primary-hover); }
|
||
.ui-btn--secondary { background: transparent; border-color: var(--color-border-strong); color: var(--color-text-primary); }
|
||
.ui-btn--secondary:hover:not(:disabled) { border-color: var(--color-action-primary); }
|
||
.ui-btn--secondary[aria-pressed="true"] {
|
||
background: var(--color-bg-selected); border-color: var(--color-action-primary); color: var(--color-action-primary);
|
||
}
|
||
.ui-btn--danger { background: var(--color-action-danger); color: var(--color-text-on-primary); }
|
||
.ui-btn--danger:hover:not(:disabled) { background: var(--color-action-danger-hover); }
|
||
.ui-btn--ghost { background: transparent; color: var(--color-action-primary); }
|
||
.ui-btn--ghost:hover:not(:disabled) { background: var(--color-bg-hover); color: var(--color-action-primary-hover); }
|
||
.ui-btn:disabled { cursor: not-allowed; color: var(--color-text-disabled); }
|
||
.ui-btn--primary:disabled, .ui-btn--danger:disabled { background: var(--color-bg-track); border-color: var(--color-border-default); }
|
||
.ui-btn[aria-busy="true"] { cursor: progress; }
|
||
/* загрузка: вид своего варианта, а не серая «выключенная» кнопка (AURA); повторный щелчок невозможен — disabled */
|
||
.ui-btn--primary[aria-busy="true"]:disabled { background: var(--color-action-primary); border-color: transparent; color: var(--color-text-on-primary); }
|
||
.ui-btn--danger[aria-busy="true"]:disabled { background: var(--color-action-danger); border-color: transparent; color: var(--color-text-on-primary); }
|
||
.ui-btn--secondary[aria-busy="true"]:disabled { color: var(--color-text-primary); }
|
||
.ui-btn--ghost[aria-busy="true"]:disabled { color: var(--color-action-primary); }
|
||
.ui-btn--primary[aria-busy="true"] .ui-spinner, .ui-btn--danger[aria-busy="true"] .ui-spinner {
|
||
border-color: color-mix(in srgb, var(--color-text-on-primary) 35%, transparent); border-top-color: var(--color-text-on-primary);
|
||
}
|
||
.ui-spinner {
|
||
width: 16px; height: 16px; border-radius: var(--radius-round);
|
||
border: 2px solid var(--color-border-strong); border-top-color: var(--color-action-primary);
|
||
animation: ui-spin 800ms linear infinite;
|
||
}
|
||
@keyframes ui-spin { to { transform: rotate(360deg); } }
|
||
@media (prefers-reduced-motion: reduce) { .ui-spinner { animation-duration: 2400ms; } }
|
||
|
||
.ui-icon-btn {
|
||
display: inline-flex; align-items: center; justify-content: center; width: var(--control-h); height: var(--control-h);
|
||
padding: 0; border: 1px solid transparent; border-radius: var(--control-radius); background: transparent; color: var(--color-text-secondary);
|
||
}
|
||
.ui-icon-btn:hover:not(:disabled) { background: var(--color-bg-hover); border-color: var(--color-border-strong); color: var(--color-text-primary); }
|
||
.ui-icon-btn:disabled { cursor: not-allowed; color: var(--color-text-disabled); }
|
||
|
||
.ui-seg {
|
||
display: inline-flex; gap: var(--space-1); padding: var(--space-1); border-radius: var(--radius-round); background: var(--color-bg-track);
|
||
}
|
||
.ui-seg__item {
|
||
display: inline-flex; align-items: center; height: 24px; padding: 0 var(--space-4); border: 1px solid transparent;
|
||
border-radius: var(--radius-round); background: transparent; color: var(--color-text-secondary);
|
||
font-size: var(--fs-body); line-height: var(--lh-body); font-weight: 400; text-decoration: none; white-space: nowrap;
|
||
}
|
||
.ui-seg__item:hover { color: var(--color-text-primary); }
|
||
.ui-seg__item[aria-checked="true"], .ui-seg__item[aria-current="page"] {
|
||
background: var(--color-bg-surface); border-color: var(--color-border-selected); color: var(--color-action-primary); font-weight: 600;
|
||
}
|
||
|
||
.ui-theme {
|
||
display: inline-flex; align-items: center; width: 44px; height: 24px; padding: 2px; border: 1px solid var(--color-border-strong);
|
||
border-radius: var(--radius-round); background: var(--color-bg-track); color: var(--color-text-secondary);
|
||
}
|
||
.ui-theme__knob {
|
||
display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: var(--radius-round);
|
||
background: var(--color-bg-surface); box-shadow: inset 0 0 0 1px var(--color-border-default); transition: transform var(--motion);
|
||
}
|
||
.ui-theme[aria-checked="true"] { border-color: var(--color-border-selected); color: var(--color-action-primary); }
|
||
.ui-theme[aria-checked="true"] .ui-theme__knob { transform: translateX(20px); }
|
||
|
||
.ui-badge {
|
||
display: inline-flex; align-items: center; height: 20px; padding: 0 var(--space-2); border: 1px solid transparent;
|
||
border-radius: var(--radius-round); font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 600; white-space: nowrap;
|
||
}
|
||
.ui-badge--neutral { background: var(--color-bg-track); border-color: var(--color-border-default); color: var(--color-text-secondary); }
|
||
.ui-badge--success { background: var(--color-status-success-bg); border-color: var(--color-status-success); color: var(--color-status-success); }
|
||
.ui-badge--error { background: var(--color-status-error-bg); border-color: var(--color-status-error); color: var(--color-status-error); }
|
||
.ui-badge--warning { background: var(--color-status-warning-bg); border-color: var(--color-status-warning-border); color: color-mix(in srgb, var(--color-status-warning) 55%, var(--color-text-primary)); }
|
||
.ui-badge--info { background: var(--color-bg-subtle); border-color: var(--color-border-strong); color: var(--color-text-primary); }
|
||
|
||
.ui-chip {
|
||
display: inline-flex; align-items: center; gap: var(--space-1); height: 24px; padding: 0 var(--space-1) 0 var(--space-3);
|
||
border: 1px solid var(--color-border-selected); border-radius: var(--radius-round); background: var(--color-bg-selected);
|
||
color: var(--color-action-primary); font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 600;
|
||
}
|
||
.ui-chip__x {
|
||
display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; padding: 0; border: 0;
|
||
border-radius: var(--radius-round); background: transparent; color: inherit;
|
||
}
|
||
.ui-chip__x:hover { background: var(--color-bg-hover); }
|
||
|
||
.ui-notice {
|
||
display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) var(--space-4);
|
||
border: 1px solid var(--color-border-default); border-radius: var(--radius-lg); background: var(--color-bg-subtle);
|
||
}
|
||
.ui-notice__icon {
|
||
display: inline-flex; flex: none; align-items: center; justify-content: center; width: 20px; height: 20px;
|
||
border-radius: var(--radius-round); color: var(--color-text-on-primary); background: var(--color-text-secondary);
|
||
}
|
||
.ui-notice__body { flex: 1; min-width: 0; }
|
||
.ui-notice__title { font-size: var(--fs-body); line-height: var(--lh-body); font-weight: 600; color: var(--color-text-primary); }
|
||
.ui-notice__text { font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--color-text-primary); }
|
||
.ui-notice__action { flex: none; }
|
||
.ui-notice a { color: var(--color-action-primary); }
|
||
.ui-notice--warning a { color: var(--color-text-primary); text-decoration: underline; }
|
||
.ui-notice--success { background: var(--color-status-success-bg); border-color: var(--color-status-success); }
|
||
.ui-notice--success .ui-notice__icon { background: var(--color-status-success); }
|
||
.ui-notice--error { background: var(--color-status-error-bg); border-color: var(--color-status-error); }
|
||
.ui-notice--error .ui-notice__icon { background: var(--color-status-error); }
|
||
.ui-notice--warning { background: var(--color-status-warning-bg); border-color: var(--color-status-warning-border); }
|
||
.ui-notice--warning .ui-notice__icon { background: var(--color-status-warning); }
|
||
|
||
.ui-panel {
|
||
background: var(--color-bg-surface); border: 1px solid var(--color-border-default); border-radius: var(--radius-xl);
|
||
}
|
||
.ui-panel--md { padding: var(--space-4); }
|
||
.ui-panel--lg { padding: var(--space-6); }
|
||
.ui-panel--none { padding: 0; }
|
||
|
||
.ui-page-header { display: flex; flex-direction: column; gap: var(--space-4); margin-bottom: var(--space-4); }
|
||
.ui-page-header__row { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
|
||
.ui-page-header__text { min-width: 0; }
|
||
.ui-page-header__title { margin: 0; font-size: var(--fs-section); line-height: var(--lh-section); font-weight: 700; color: var(--color-text-primary); }
|
||
.ui-page-header__subtitle { margin: var(--space-1) 0 0; font-size: var(--fs-body); line-height: var(--lh-body); color: var(--color-text-secondary); }
|
||
.ui-page-header__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
|
||
|
||
/* поля */
|
||
.ui-field { display: flex; flex-direction: column; gap: var(--space-1); }
|
||
.ui-field__label { font-size: var(--fs-body); line-height: var(--lh-body); font-weight: 600; color: var(--color-text-primary); }
|
||
.ui-field__hint { font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--color-text-secondary); }
|
||
.ui-field__error { font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--color-status-error); }
|
||
|
||
.ui-input {
|
||
box-sizing: border-box; width: 100%; height: var(--control-h); padding: 0 var(--space-3);
|
||
border: 1px solid var(--color-border-default); border-radius: var(--control-radius);
|
||
background: var(--color-bg-surface); color: var(--color-text-primary);
|
||
font: inherit; font-family: var(--font-ui); font-size: var(--fs-body); line-height: var(--lh-body);
|
||
transition: border-color var(--motion), box-shadow var(--motion);
|
||
}
|
||
.ui-input--lg { height: var(--control-h-lg); }
|
||
.ui-input::placeholder { color: var(--color-text-placeholder); }
|
||
.ui-input:hover:not(:disabled) { border-color: var(--color-border-strong); }
|
||
.ui-input:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; border-color: var(--color-action-primary); }
|
||
.ui-input[aria-invalid="true"] { border-color: var(--color-status-error); }
|
||
.ui-input:disabled { cursor: not-allowed; color: var(--color-text-disabled); background: var(--color-bg-track); }
|
||
.ui-textarea { height: auto; min-height: 80px; padding: var(--space-2) var(--space-3); resize: vertical; }
|
||
|
||
.ui-search { position: relative; display: inline-flex; align-items: center; width: 100%; }
|
||
.ui-search__icon { position: absolute; left: var(--space-3); display: inline-flex; color: var(--color-text-secondary); pointer-events: none; }
|
||
.ui-search__input { padding-left: calc(var(--space-3) + 24px); padding-right: calc(var(--space-2) + var(--control-h)); }
|
||
.ui-search__input::-webkit-search-cancel-button { display: none; }
|
||
.ui-search__clear { position: absolute; right: 0; }
|
||
|
||
.ui-select { position: relative; display: inline-flex; align-items: center; width: 100%; }
|
||
.ui-select__control { appearance: none; padding-right: calc(var(--space-3) + 24px); cursor: pointer; }
|
||
.ui-select--lg .ui-select__control { height: var(--control-h-lg); }
|
||
.ui-select__chevron { position: absolute; right: var(--space-3); display: inline-flex; color: var(--color-text-secondary); pointer-events: none; }
|
||
|
||
.ui-check { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--fs-body); line-height: var(--lh-body); color: var(--color-text-primary); cursor: pointer; }
|
||
.ui-check__box { width: 16px; height: 16px; margin: 0; border-radius: var(--radius-xs); accent-color: var(--color-action-primary); cursor: pointer; }
|
||
.ui-check__box:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; }
|
||
|
||
/* окно */
|
||
.ui-dialog {
|
||
box-sizing: border-box; max-width: none; max-height: 90vh; padding: var(--space-6); overflow: auto;
|
||
border: 1px solid var(--color-border-default); border-radius: var(--radius-2xl);
|
||
background: var(--color-bg-surface); color: var(--color-text-primary); box-shadow: var(--shadow-dialog);
|
||
font-family: var(--font-ui);
|
||
}
|
||
.ui-dialog::backdrop { background: var(--color-overlay); }
|
||
.ui-dialog--sm { width: min(480px, calc(100vw - 32px)); }
|
||
.ui-dialog--md { width: min(640px, calc(100vw - 32px)); }
|
||
.ui-dialog--lg { width: min(1000px, calc(100vw - 32px)); }
|
||
@media (max-width: 700px) {
|
||
.ui-dialog { width: 100vw; max-width: 100vw; height: 100vh; max-height: 100vh; margin: 0; border-radius: 0; }
|
||
}
|
||
|
||
/* выпадающая панель и меню: пункты — кнопки; «выбрано» — цветом текста и галочкой */
|
||
.dd { position: relative; display: inline-block; }
|
||
.dd-menu {
|
||
position: absolute; top: calc(100% + var(--space-1)); left: 0; z-index: 20; min-width: 280px; max-width: 420px;
|
||
display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-2);
|
||
background: var(--color-bg-surface); border: 1px solid var(--color-border-default); border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-popover); color: var(--color-text-primary); font-family: var(--font-ui);
|
||
}
|
||
.dd-menu.menu { right: 0; left: auto; min-width: 230px; gap: 0; padding: var(--space-1); }
|
||
.dd-list { max-height: 260px; overflow: auto; display: flex; flex-direction: column; }
|
||
.dd-item {
|
||
display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm);
|
||
font-size: var(--fs-body); line-height: var(--lh-body); cursor: pointer;
|
||
}
|
||
.dd-item:hover { background: var(--color-bg-hover); }
|
||
.dd-item input { flex: none; width: 16px; height: 16px; margin: 0; accent-color: var(--color-action-primary); cursor: pointer; }
|
||
.dd-item input:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; }
|
||
.dd-item .n { margin-left: auto; font-size: var(--fs-caption); color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
|
||
.dd-empty { padding: var(--space-2); font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--color-text-secondary); }
|
||
.dd-foot { display: flex; gap: var(--space-2); justify-content: space-between; border-top: 1px solid var(--color-border-default); padding-top: var(--space-2); }
|
||
.dd--start > .dd-menu.menu { left: 0; right: auto; }
|
||
.dd--up > .dd-menu { top: auto; bottom: calc(100% + var(--space-1)); }
|
||
.menu button {
|
||
display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); width: 100%;
|
||
min-height: var(--control-h); padding: var(--space-1) var(--space-3); border: 0; border-radius: var(--radius-sm);
|
||
background: none; color: var(--color-text-primary); font: inherit; font-family: var(--font-ui);
|
||
font-size: var(--fs-body); line-height: var(--lh-body); text-align: left; cursor: pointer;
|
||
transition: background var(--motion), color var(--motion);
|
||
}
|
||
.menu button:hover { background: var(--color-bg-hover); }
|
||
.menu button:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: -2px; background: var(--color-bg-hover); }
|
||
.menu button[aria-pressed="true"] { color: var(--color-action-primary); }
|
||
.menu button[aria-pressed="true"]::after { content: "✓"; font-weight: 600; }
|
||
.menu button.danger { color: var(--color-status-error); }
|
||
.menu hr { border: 0; border-top: 1px solid var(--color-border-default); margin: var(--space-1) 0; }
|
||
|
||
/* таблица: внутри <Panel padding="none" className="ui-table-wrap">; шапка липнет; строки 44 px, hover — фоном.
|
||
Узко — прокрутка внутри обёртки (@container page), а не превращение в карточки; там шапка не липнет (липкость
|
||
внутри прокручиваемой обёртки сдвинула бы её на --topbar-h поверх первой строки).
|
||
position: relative — чтобы .visually-hidden в ячейках не растягивал страницу за правый край. */
|
||
.ui-table-wrap { position: relative; overflow: clip; }
|
||
/* --table-min: ширина, уже которой таблица не сжимается, а прокручивается (style={{"--table-min": "760px"}}) */
|
||
.ui-table { width: 100%; min-width: var(--table-min, 0); border-collapse: separate; border-spacing: 0; font-size: var(--fs-body); line-height: var(--lh-body); }
|
||
.ui-table th, .ui-table td {
|
||
height: 44px; padding: 0 var(--space-3); text-align: left; vertical-align: middle; border-bottom: 1px solid var(--color-border-default);
|
||
}
|
||
.ui-table tbody tr:last-child td { border-bottom: 0; }
|
||
.ui-table th {
|
||
position: sticky; top: var(--topbar-h, 0); z-index: 1; background: var(--color-bg-surface); color: var(--color-text-secondary);
|
||
font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 600; white-space: nowrap;
|
||
}
|
||
.ui-table th:first-child { border-top-left-radius: calc(var(--radius-xl) - 1px); }
|
||
.ui-table th:last-child { border-top-right-radius: calc(var(--radius-xl) - 1px); }
|
||
.ui-table td { color: var(--color-text-primary); }
|
||
.ui-table td.num, .ui-table th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
||
.ui-table tbody tr.is-clickable { cursor: pointer; }
|
||
/* постоянные действия строки — IconButton в последней ячейке, справа */
|
||
.ui-table td.actions { width: 1%; white-space: nowrap; text-align: right; }
|
||
.ui-table td.actions .ui-icon-btn + .ui-icon-btn { margin-left: var(--space-1); }
|
||
.ui-table tbody tr:hover td { background: var(--color-bg-hover); }
|
||
/* после правил шапки: при равной специфичности побеждает то, что ниже */
|
||
@container page (max-width: 1051.98px) {
|
||
.ui-table-wrap { overflow-x: auto; }
|
||
.ui-table th { position: static; }
|
||
}
|
||
/* просрочка/срок: цвет ошибки и полужирный у самой даты, без заливки строки; рядом visually-hidden «, просрочено» */
|
||
.ui-late { color: var(--color-status-error); font-weight: 600; }
|
||
/* пустое состояние: по центру панели, вторичным текстом, с действием */
|
||
.ui-empty {
|
||
display: flex; flex-direction: column; align-items: center; gap: var(--space-3); padding: var(--space-8) var(--space-4);
|
||
text-align: center; color: var(--color-text-secondary);
|
||
}
|
||
.ui-empty__title { font-size: var(--fs-lg); line-height: var(--lh-lg); font-weight: 700; color: var(--color-text-primary); }
|
||
|
||
/* строка фильтров над списком: поиск растягивается (до 480), остальное — по содержимому, перенос по ширине */
|
||
.ui-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-bottom: var(--space-4); }
|
||
.ui-toolbar > .ui-search { flex: 1 1 280px; max-width: 480px; }
|
||
@container page (max-width: 607.98px) { .ui-toolbar > .ui-search { max-width: none; } }
|
||
.ui-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: calc(-1 * var(--space-2)) 0 var(--space-4); }
|
||
|
||
/* форма и окно: поля столбцом с шагом 16, кнопки внизу справа («Отмена» слева от основной) */
|
||
.ui-form { display: flex; flex-direction: column; gap: var(--space-4); }
|
||
.ui-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }
|
||
.ui-dialog__title { margin: 0 0 var(--space-4); font-size: var(--fs-lg); line-height: var(--lh-lg); font-weight: 700; }
|
||
.ui-dialog__text { margin: 0 0 var(--space-2); color: var(--color-text-secondary); }
|
||
|
||
/* цветная точка категории: <span class="ui-dot" style="--dot: var(--color-cat-3)"></span> */
|
||
.ui-dot { display: inline-block; flex: none; width: 8px; height: 8px; border-radius: var(--radius-round); background: var(--dot, currentColor); vertical-align: middle; }
|