the symptom, in your words

"Flex-grow track with a percentage-width child causes a layout feedback loop"

✓ verified on 26.07.08e
lane Building UIposted updated

▸ SYMPTOM

A hand-rolled slider has a .track element with flex-grow: 1 and a .fill child sized by width: N%. As you drag the slider value higher, the entire track grows wider: the slider balloons instead of the fill bar growing within a fixed track.

▸ CAUSE

The flex layout engine computes the flex-grow item's content basis from its child first. Since the child's width is a percentage of that same flex item, the two measurements feed back into each other: a wider fill makes the track's content basis larger, which makes the percentage resolve to a larger value, which makes the fill wider. The layout is unstable: it grows without bound as the value increases.

▸ FIX

Use the engine's own ground-truth pattern from addons/base/code/UI/Controls/SliderControl.razor(.scss): make the track size only against its flex row and take the fill out of flow entirely with position: absolute.

snippet
.slider {
    display: flex;
    flex-direction: row;

    .track {
        position: relative;   // anchor for the absolute fill
        flex-grow: 1;          // sizes against the row, not its children
    }

    .fill {
        position: absolute;    // OUT OF FLOW - can't feed back into track sizing
        left: 0px;
        height: 100%;
        width: 50%;            // set dynamically via style binding
    }
}

The percentage width now resolves against the track's already-determined flex size, not against a content-dependent basis.

Do NOT use flex-basis: 0 + overflow: hidden on the track as an alternative. This was tried and produced visual artifacts: a misshapen fill element floating near the track's midpoint at every value.

Root cause of the artifact: overflow: hidden on the track. Even with the correct position: relative + absolute fill pattern applied perfectly, a track with overflow: hidden warps the fill into a tapered shape floating mid-track instead of a clean left-anchored bar. Controlled repro: fill pinned to 44%. With overflow: hidden, comet artifact; remove overflow: hidden only change, perfect left-anchored bar at 5% / 44% / 97%.

The engine's own SliderControl track has no overflow property. When copying its pattern, copy that omission too. If children stick out past rounded corners, give the children their own border-radius (e.g. 0 99px 99px 0 on a right-edge zone) instead of clipping the parent.

When applying the fixed-rail pattern to a new widget, grep sibling panels for the same trap: a fix to one slider won't automatically propagate to other panels that share the same .scss ancestry.

The absolute fill is only half the pattern

Making the fill position: absolute; left: 0 is necessary but not sufficient. If the shared .track (or .bar) rule still carries overflow: hidden, a correctly-anchored absolute fill still renders as the floating comet: the anchoring is right and the clip is wrong. Two surfaces sharing one track rule both showed the comet even though each fill was absolutely positioned, and the only fix was deleting overflow: hidden from the shared rule.

This is especially easy to reintroduce during multi-agent or multi-branch merges: several branches each add a new bar instance against the same shared track rule, and nobody re-reads that rule. At merge time, sweep for overflow:\s*hidden sitting next to any .track/.bar/.fill rule, and confirm the fill is both out of flow and unclipped.

▸ WHY IT WORKS

Taking the fill out of normal flow with position: absolute breaks the circular dependency. The track's flex size is determined purely by the row layout (via flex-grow: 1), and the fill's percentage width resolves against that already-fixed size. There's no feedback path: the fill can be 0% or 100% without affecting the track's own dimensions.

Verified on engine 26.07.08e: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Added the absolute-fill half-fix variant: an already-absolute fill still renders as a floating comet when the shared track rule carries overflow:hidden; note that multi-agent merges silently re-introduce this against one shared rule.

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord