Skip to content

Commit 345083a

Browse files
authored
fix(ui): uniform 32px control height across settings clusters (#106898)
Inputs (41px), buttons (30-38px), and segmented switches (33px) sat at mixed heights inside the same settings rows and toolbars. All controls in settings clusters now share --settings-control-height (32px), and the eight remaining native selects (cron filter popover, model-provider default/fallback pickers, usage session sort) adopt .settings-select. Row control clusters wrap only at mobile widths where they have a definite width (Chromium collapses wrappable shrink-to-fit flex containers to their widest item).
1 parent c302b4b commit 345083a

6 files changed

Lines changed: 62 additions & 2 deletions

File tree

ui/src/pages/cron/view.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ function renderJobsFilterPopover(props: CronProps, active: boolean) {
555555
<label class="field">
556556
<span>${t("cron.jobs.schedule")}</span>
557557
<select
558+
class="settings-select"
558559
data-test-id="cron-jobs-schedule-filter"
559560
.value=${props.jobsScheduleKindFilter}
560561
@change=${(e: Event) =>
@@ -572,6 +573,7 @@ function renderJobsFilterPopover(props: CronProps, active: boolean) {
572573
<label class="field">
573574
<span>${t("cron.jobs.lastRun")}</span>
574575
<select
576+
class="settings-select"
575577
data-test-id="cron-jobs-last-status-filter"
576578
.value=${props.jobsLastStatusFilter}
577579
@change=${(e: Event) =>
@@ -590,6 +592,7 @@ function renderJobsFilterPopover(props: CronProps, active: boolean) {
590592
<label class="field">
591593
<span>${t("cron.jobs.sort")}</span>
592594
<select
595+
class="settings-select"
593596
.value=${props.jobsSortBy}
594597
@change=${(e: Event) =>
595598
props.onJobsFiltersChange({
@@ -604,6 +607,7 @@ function renderJobsFilterPopover(props: CronProps, active: boolean) {
604607
<label class="field">
605608
<span>${t("cron.jobs.direction")}</span>
606609
<select
610+
class="settings-select"
607611
.value=${props.jobsSortDir}
608612
@change=${(e: Event) =>
609613
props.onJobsFiltersChange({

ui/src/pages/model-providers/default-models-view.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export function renderDefaultModels(props: DefaultModelsViewProps) {
8383
<label class="field">
8484
<span>${t("modelProviders.defaults.primary")}</span>
8585
<select
86+
class="settings-select"
8687
.value=${props.selection.primary}
8788
?disabled=${disabled || saving}
8889
title=${title}
@@ -98,6 +99,7 @@ export function renderDefaultModels(props: DefaultModelsViewProps) {
9899
<label class="field">
99100
<span>${t("modelProviders.defaults.utility")}</span>
100101
<select
102+
class="settings-select"
101103
.value=${props.selection.utilityModel ?? AUTOMATIC_UTILITY_VALUE}
102104
?disabled=${disabled || saving}
103105
title=${title}
@@ -139,6 +141,7 @@ export function renderDefaultModels(props: DefaultModelsViewProps) {
139141
<label class="field model-providers__fallback-add">
140142
<span>${t("modelProviders.defaults.addFallback")}</span>
141143
<select
144+
class="settings-select"
142145
.value=${""}
143146
?disabled=${disabled || saving || !props.selection.primary}
144147
title=${title}

ui/src/pages/model-providers/view.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ function renderAddProvider(props: ModelProvidersViewProps) {
399399
<label class="field">
400400
<span>${t("modelProviders.add.provider")}</span>
401401
<select
402+
class="settings-select"
402403
.value=${props.addProviderId}
403404
@change=${(event: Event) =>
404405
props.onAddProviderIdChange((event.target as HTMLSelectElement).value)}

ui/src/pages/usage/view-overview.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,7 @@ function renderSessionsCard(
10531053
<label class="sessions-sort">
10541054
<span>${t("usage.sessions.sort")}</span>
10551055
<select
1056+
class="settings-select"
10561057
@change=${(e: Event) =>
10571058
onSessionSortChange((e.target as HTMLSelectElement).value as typeof sessionSort)}
10581059
>

ui/src/styles/cron.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@
124124
flex-wrap: wrap;
125125
}
126126

127+
/* Toolbar buttons share the settings control height with the adjacent
128+
segmented switches, search input, and filter selects. */
129+
.cron-toolbar .btn {
130+
height: var(--settings-control-height);
131+
padding-block: 0;
132+
box-sizing: border-box;
133+
}
134+
127135
.cron-search-box {
128136
position: relative;
129137
flex: 1 1 220px;

ui/src/styles/settings.css

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
:root {
2+
--settings-control-height: 32px;
3+
}
4+
15
/* ── Settings design language ──────────────────────────────────────────────
26
*
37
* One structural pattern for every settings surface:
@@ -180,11 +184,13 @@
180184

181185
.settings-row__control {
182186
display: flex;
183-
flex-wrap: wrap;
187+
/* No wrap here: Chromium sizes a wrappable shrink-to-fit flex container to
188+
its widest item, collapsing multi-control clusters. The <=640px block
189+
gives the cluster a definite width and enables wrapping there. */
184190
align-items: center;
185191
justify-content: flex-end;
186192
gap: var(--space-2);
187-
flex: 0 1 auto;
193+
flex: 0 0 auto;
188194
min-width: 0;
189195
max-width: 60%;
190196
}
@@ -275,6 +281,40 @@
275281

276282
/* ── Controls ── */
277283

284+
/* One control height inside settings clusters. Buttons, inputs, selects, and
285+
segmented controls share 32px so mixed rows never show ragged heights.
286+
Multi-select list boxes and textareas keep their natural height. */
287+
.settings-row__control .btn,
288+
.settings-section__actions .btn,
289+
.settings-group .data-table-search input,
290+
input.settings-input,
291+
select.settings-select:not([multiple]) {
292+
height: var(--settings-control-height);
293+
padding-block: 0;
294+
box-sizing: border-box;
295+
}
296+
297+
/* Segmented keeps its 2px track inset; the buttons fill the inner height.
298+
min-height (not height) so many-option enums can still wrap to two lines. */
299+
.settings-segmented {
300+
min-height: var(--settings-control-height);
301+
box-sizing: border-box;
302+
align-items: stretch;
303+
}
304+
305+
.settings-segmented > .settings-segmented__btn {
306+
display: inline-flex;
307+
align-items: center;
308+
padding-block: 0;
309+
}
310+
311+
.settings-row__control .btn--icon,
312+
.settings-section__actions .btn--icon {
313+
width: var(--settings-control-height);
314+
min-width: var(--settings-control-height);
315+
padding-inline: 0;
316+
}
317+
278318
.settings-toggle {
279319
display: inline-flex;
280320
align-items: center;
@@ -526,6 +566,9 @@ textarea.settings-input {
526566
}
527567

528568
.settings-row__control {
569+
/* Definite width makes flex-wrap size lines correctly (see desktop note). */
570+
width: 100%;
529571
max-width: 100%;
572+
flex-wrap: wrap;
530573
}
531574
}

0 commit comments

Comments
 (0)