fix(portal): align stepper labels with circles via per-unit absolute positioning

Original macro put the 5 labels in a separate flex container below the
stepper with flex:1 each. That distributes them at 10%/30%/50%/70%/90%
(centred in 1/5 slots) while the circles distribute at 0%/25%/50%/75%/
100% (edges via space-between + line-flex). Result: labels visibly off
from their circles, getting worse the wider the row.

Restructured the macro so each circle + its label live inside a single
.o_fp_step_unit. The label is absolute-positioned at top:100% / left:50%
with translateX(-50%), so its horizontal centre always pins to the
circle's centre regardless of text width. Wider labels ('Inspected')
overflow equally to both sides instead of pushing the column.

Bumped stepper margin-bottom to 2.4rem so the absolutely-positioned
labels have clearance below. Dropped the now-unused .o_fp_step_labels
container rule.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-17 04:10:28 -04:00
parent a63fbe1558
commit 27badff570
2 changed files with 71 additions and 55 deletions

View File

@@ -8,7 +8,19 @@
.o_fp_stepper {
display: flex;
align-items: center;
margin-bottom: .35rem;
// Bottom space for the absolutely-positioned labels below each circle.
// ~2.2rem covers two short lines (title + time_label).
margin-bottom: 2.4rem;
// Each unit holds one circle + its label. The label is absolutely
// positioned (see .o_fp_step_label below) so its horizontal centre
// lines up with the circle's centre even when text is wider than 24px.
.o_fp_step_unit {
position: relative;
flex-shrink: 0;
width: 24px;
height: 24px;
}
.o_fp_step_circle {
width: 24px;
@@ -20,7 +32,6 @@
font-family: $fp-font;
font-size: .65rem;
font-weight: 700;
flex-shrink: 0;
background: $fp-card-bg;
border: 1.5px solid $fp-card-border;
color: $fp-muted-light;
@@ -58,6 +69,40 @@
&.o_fp_step_line_done { background: $fp-teal; }
&.o_fp_step_line_warn { background: $fp-amber; }
}
// Label centred on its circle via absolute positioning. Wider text
// ("Inspected") overflows equally left + right instead of pushing
// the column or sitting in a separate flex slot.
.o_fp_step_label {
position: absolute;
top: calc(100% + .45rem);
left: 50%;
transform: translateX(-50%);
text-align: center;
white-space: nowrap;
font-size: .68rem;
.o_fp_step_label_title {
color: $fp-muted-light;
font-weight: 500;
}
.o_fp_step_label_time {
color: $fp-disabled;
font-size: .6rem;
}
&.o_fp_step_label_done {
.o_fp_step_label_title { color: $fp-text-body; }
.o_fp_step_label_time { color: $fp-muted-light; }
}
&.o_fp_step_label_active {
.o_fp_step_label_title { color: $fp-teal; font-weight: 700; }
.o_fp_step_label_time { color: $fp-teal; }
}
&.o_fp_step_label_active_warn {
.o_fp_step_label_title { color: $fp-amber-text; font-weight: 700; }
.o_fp_step_label_time { color: $fp-amber-text; }
}
}
}
// Pulsing glow for the active step indicator. Kept subtle - the ring
@@ -83,34 +128,5 @@
}
}
// Step labels row below the stepper
.o_fp_step_labels {
display: flex;
justify-content: space-between;
font-size: .68rem;
.o_fp_step_label {
text-align: center;
flex: 1;
.o_fp_step_label_title {
color: $fp-muted-light;
font-weight: 500;
}
.o_fp_step_label_time {
color: $fp-disabled;
font-size: .6rem;
}
&.o_fp_step_label_done {
.o_fp_step_label_title { color: $fp-text-body; }
.o_fp_step_label_time { color: $fp-muted-light; }
}
&.o_fp_step_label_active {
.o_fp_step_label_title { color: $fp-teal; font-weight: 700; }
.o_fp_step_label_time { color: $fp-teal; }
}
&.o_fp_step_label_active_warn {
.o_fp_step_label_title { color: $fp-amber-text; font-weight: 700; }
.o_fp_step_label_time { color: $fp-amber-text; }
}
}
}
// Legacy .o_fp_step_labels container removed — labels are now nested
// inside each .o_fp_step_unit (see above) so they centre on their circle.

View File

@@ -27,17 +27,29 @@
<t t-set="active_state" t-value="active_state or 'normal'"/>
<div class="o_fp_stepper">
<t t-foreach="steps" t-as="step">
<!-- circle -->
<div t-attf-class="o_fp_step_circle #{
'o_fp_step_done' if step['status'] == 'done' else
(('o_fp_step_active_warn' if active_state == 'warn' else 'o_fp_step_active') if step['status'] == 'active' else '')
}">
<t t-if="step['status'] == 'done'"></t>
<t t-elif="step['status'] in ('active', 'pending')">
<t t-out="step_index + 1"/>
</t>
<!-- Unit = circle + its label stacked. Label is absolutely
positioned below the circle (in SCSS) so its horizontal
centre lines up with the circle no matter how wide the
text is — fixes the column-vs-edge distribution
mismatch we had with a separate labels row. -->
<div class="o_fp_step_unit">
<div t-attf-class="o_fp_step_circle #{
'o_fp_step_done' if step['status'] == 'done' else
(('o_fp_step_active_warn' if active_state == 'warn' else 'o_fp_step_active') if step['status'] == 'active' else '')
}">
<t t-if="step['status'] == 'done'"></t>
<t t-elif="step['status'] in ('active', 'pending')">
<t t-out="step_index + 1"/>
</t>
</div>
<div t-attf-class="o_fp_step_label #{
'o_fp_step_label_done' if step['status'] == 'done' else
(('o_fp_step_label_active_warn' if active_state == 'warn' else 'o_fp_step_label_active') if step['status'] == 'active' else '')
}">
<div class="o_fp_step_label_title" t-out="step['label']"/>
<div class="o_fp_step_label_time" t-out="step.get('time_label') or ''"/>
</div>
</div>
<!-- connecting line (omit after last circle) -->
<t t-if="not step_last">
<div t-attf-class="o_fp_step_line #{
'o_fp_step_line_done' if step['status'] == 'done' else
@@ -46,18 +58,6 @@
</t>
</t>
</div>
<!-- Labels under -->
<div class="o_fp_step_labels">
<t t-foreach="steps" t-as="step">
<div t-attf-class="o_fp_step_label #{
'o_fp_step_label_done' if step['status'] == 'done' else
(('o_fp_step_label_active_warn' if active_state == 'warn' else 'o_fp_step_label_active') if step['status'] == 'active' else '')
}">
<div class="o_fp_step_label_title" t-out="step['label']"/>
<div class="o_fp_step_label_time" t-out="step.get('time_label') or ''"/>
</div>
</t>
</div>
</template>
<!-- ================================================================== -->