diff --git a/src/core/elementNode.ts b/src/core/elementNode.ts
index 2a54d1b..e4f8505 100644
--- a/src/core/elementNode.ts
+++ b/src/core/elementNode.ts
@@ -1663,18 +1663,20 @@ export class ElementNode {
flexDirection === 'row' || flexDirection === 'row-reverse';
flexFitsWidth = isFlexRow && node.flexBoundary !== 'fixed';
- if (isDev && flexFitsWidth) {
- const needsWidthForJustify = [
- 'center',
- 'flex-end',
- 'space-between',
- 'space-around',
- 'space-evenly',
- ].includes(node.justifyContent as string);
-
- if (needsWidthForJustify) {
+ // Every justify mode except flexStart positions children out of the
+ // container's free space. Shrinking to fit leaves none, so fall back
+ // to filling the parent unless the developer explicitly asked to
+ // contain — in which case honor it and warn about the contradiction.
+ if (
+ flexFitsWidth &&
+ node.justifyContent !== undefined &&
+ node.justifyContent !== 'flexStart'
+ ) {
+ if (node.flexBoundary === undefined) {
+ flexFitsWidth = false;
+ } else if (isDev) {
console.warn(
- `justifyContent '${node.justifyContent}' requires an explicit width on the flex container; without one it shrinks to fit its children and has no free space to distribute: `,
+ `justifyContent '${node.justifyContent}' has no free space to distribute on a flexBoundary 'contain' container without an explicit width: `,
this,
);
}
diff --git a/tests/flex-justify-width.test.tsx b/tests/flex-justify-width.test.tsx
new file mode 100644
index 0000000..db916a1
--- /dev/null
+++ b/tests/flex-justify-width.test.tsx
@@ -0,0 +1,98 @@
+import * as v from 'vitest'
+import * as lng from '@solidtv/solid'
+
+import {renderer, waitForUpdate} from './setup.js'
+
+const PARENT = 600
+const CHILD = 100
+
+v.test('flex row without width fills parent when justifyContent needs free space', async () => {
+ let center!: lng.ElementNode
+ let centerOne!: lng.ElementNode
+ let between!: lng.ElementNode
+ let betweenTwo!: lng.ElementNode
+
+ const dispose = renderer.render(() => (
+
+
+
+
+
+
+
+
+
+
+ ))
+
+ await waitForUpdate()
+
+ // Container fills the parent instead of shrinking to its children...
+ v.assert.equal(center.width, PARENT)
+ // ...so there is free space to center into: (600 - 200) / 2
+ v.assert.equal(centerOne.x, (PARENT - CHILD * 2) / 2)
+
+ // spaceBetween pushes the last child to the far edge rather than to a
+ // negative offset against a zero-width container.
+ v.assert.equal(between.width, PARENT)
+ v.assert.equal(betweenTwo.x, PARENT - CHILD)
+
+ dispose()
+})
+
+v.test('flex row without width still shrinks to fit for flexStart and no justifyContent', async () => {
+ let implicit!: lng.ElementNode
+ let start!: lng.ElementNode
+
+ const dispose = renderer.render(() => (
+
+
+
+
+
+
+
+
+
+
+ ))
+
+ await waitForUpdate()
+
+ v.assert.equal(implicit.width, CHILD * 2)
+ v.assert.equal(start.width, CHILD * 2)
+
+ dispose()
+})
+
+v.test('an explicit flexBoundary contain wins over justifyContent and warns', async () => {
+ const warn = v.vi.spyOn(console, 'warn').mockImplementation(() => {})
+ let contained!: lng.ElementNode
+
+ const dispose = renderer.render(() => (
+
+
+
+
+
+
+ ))
+
+ await waitForUpdate()
+
+ // The explicit contain is honored — the container is not widened to the parent.
+ v.assert.notEqual(contained.width, PARENT)
+ v.assert.isTrue(
+ warn.mock.calls.some((args) => String(args[0]).includes('justifyContent')),
+ 'expected a dev warning about the contradiction',
+ )
+
+ warn.mockRestore()
+ dispose()
+})