Install ReUI (MCP, skill, @reui registry, Tailwind v4, shadcn base-nova) and move the shared UI onto it: Button, Tag, Check, Input/Textarea, Segmented, Alert, Panel, Dialog, Tooltip, Menu and Select (Popover), Toast (sonner), the Table frame (DataGrid), the Dashboard KPI strip, the shnt Ops palette, Geist and the type scale. Design only: behaviour, texts and roles are unchanged. Tests that asserted the old DOM were updated; the tests now format numbers in English wherever they run. docs/reui-migration-plan.md records the plan and its decisions. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
147 lines
3.9 KiB
CSS
147 lines
3.9 KiB
CSS
/*
|
|
The trigger is the same hole as a text field: in a form, choosing and typing have
|
|
to weigh the same. What changes is that this one opens.
|
|
*/
|
|
|
|
.trigger {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--s-6);
|
|
width: 100%;
|
|
min-height: var(--field-height);
|
|
padding: var(--s-5) var(--s-7);
|
|
border: 1px solid var(--line-field);
|
|
border-radius: var(--r-3);
|
|
background-color: var(--field);
|
|
color: var(--ink);
|
|
font: inherit;
|
|
font-size: var(--t-4);
|
|
/* `font: inherit` brought the body's 1.55 and the trigger came out 37.4 px:
|
|
three px taller than the text field beside it (see `--field-line`). */
|
|
line-height: var(--field-line);
|
|
text-align: left;
|
|
cursor: pointer;
|
|
box-shadow: var(--sunken);
|
|
transition: border-color var(--t-fast), box-shadow var(--t-fast);
|
|
}
|
|
|
|
.trigger:hover:not(:disabled) { border-color: var(--line-ctrl); }
|
|
|
|
.trigger:focus-visible,
|
|
.trigger[aria-expanded='true'] {
|
|
outline: none;
|
|
border-color: var(--acc-ink);
|
|
box-shadow: var(--sunken), var(--halo-focus);
|
|
}
|
|
|
|
.trigger:disabled { opacity: 0.45; cursor: default; }
|
|
|
|
.value { composes: truncated from './text.module.css'; }
|
|
.emptyText { color: var(--faint); }
|
|
|
|
.chevron { color: var(--mute); flex: none; transition: transform var(--t-fast); }
|
|
.trigger[aria-expanded='true'] .chevron { transform: rotate(180deg); }
|
|
|
|
/*
|
|
The list. It is genuinely raised (two steps above the field) because it floats
|
|
over the form and has to read as something in front.
|
|
*/
|
|
.list {
|
|
/* The surface (position, border, shadow) is the Popover's. */
|
|
max-height: 280px;
|
|
scrollbar-width: thin;
|
|
overflow-y: auto;
|
|
padding: var(--s-3);
|
|
}
|
|
|
|
.option {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--s-5);
|
|
padding: var(--s-4) var(--s-5);
|
|
border-radius: var(--r-2);
|
|
font-size: var(--t-4);
|
|
color: var(--ink);
|
|
cursor: pointer;
|
|
/* Only an option wider than the window wraps, between words; the rest fit on one line. */
|
|
overflow-wrap: break-word;
|
|
}
|
|
|
|
.option[data-active='true'] { background: var(--hover-strong); }
|
|
.option[aria-selected='true'] { color: var(--acc-ink); font-weight: 600; }
|
|
.option[aria-disabled='true'] { color: var(--faint); cursor: default; }
|
|
.option[aria-disabled='true'][data-active='true'] { background: none; }
|
|
|
|
/* Fixed room for the tick: without it, choosing would shift the text. */
|
|
.brand {
|
|
display: inline-flex;
|
|
justify-content: center;
|
|
width: 14px;
|
|
flex: none;
|
|
color: var(--acc-ink);
|
|
}
|
|
|
|
.isEmpty { color: var(--faint); }
|
|
|
|
/*
|
|
`searchable` (the console's own, see `Select.tsx`): the box holds the search field
|
|
on top and the listbox under it, and only the listbox scrolls, so the field stays
|
|
in view however far down the options go.
|
|
*/
|
|
.searching {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--s-3);
|
|
overflow: hidden;
|
|
}
|
|
.scroll {
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
scrollbar-width: thin;
|
|
}
|
|
|
|
/* The field of `ui/Field`, one step smaller: it filters the list, it is not a value of the form. */
|
|
.search {
|
|
flex: none;
|
|
width: 100%;
|
|
min-width: 0;
|
|
min-height: var(--field-height);
|
|
padding: var(--s-4) var(--s-6);
|
|
border: 1px solid var(--line-field);
|
|
border-radius: var(--r-2);
|
|
background-color: var(--field);
|
|
color: var(--ink);
|
|
font: inherit;
|
|
font-size: var(--t-4);
|
|
line-height: var(--field-line);
|
|
box-shadow: var(--sunken);
|
|
}
|
|
.search::placeholder { color: var(--faint); }
|
|
.search:focus {
|
|
outline: none;
|
|
border-color: var(--acc-ink);
|
|
background-color: var(--field-focus);
|
|
}
|
|
|
|
/* An `<optgroup>`'s label: a heading of the options under it, not one of them. */
|
|
.groupLabel {
|
|
composes: smallCaps from './label.module.css';
|
|
margin-top: var(--s-3);
|
|
padding: var(--s-4) var(--s-5) var(--s-2);
|
|
border-top: 1px solid var(--line);
|
|
cursor: default;
|
|
}
|
|
|
|
/* The search matched nothing: the line where the options would be. */
|
|
.none {
|
|
padding: var(--s-4) var(--s-5);
|
|
font-size: var(--t-4);
|
|
color: var(--mute);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.trigger,
|
|
.chevron { transition: none; }
|
|
}
|