diff --git a/MarkupString.Html/Emitters/LayoutHtml.cs b/MarkupString.Html/Emitters/LayoutHtml.cs
index 4169b22..9c3d5ab 100644
--- a/MarkupString.Html/Emitters/LayoutHtml.cs
+++ b/MarkupString.Html/Emitters/LayoutHtml.cs
@@ -107,11 +107,14 @@ private static void Rule(Rule rule, string kind, BorderStyle? border, HtmlLayout
private static void Flex(Flex flex, HtmlLayoutWriter html)
{
+ var items = flex.Items.IsDefault ? [] : flex.Items;
+ var spacing = flex.Separator is { } separator ? separator.DisplayWidth : Math.Max(0, flex.Gap);
+ var reserved = flex.Vertical ? 0 : spacing * Math.Max(0, items.Length - 1);
html.Write("
");
- foreach (var block in flex.Items.IsDefault ? [] : flex.Items)
+ foreach (var block in items)
{
// Each item asks for its terminal width and grows to fill its row, so items that do not fit
// side by side wrap onto rows of their own. Spare width is left alone only when the layout
@@ -145,6 +148,8 @@ private static void Flex(Flex flex, HtmlLayoutWriter html)
html.Write(item.Basis.Kind switch
{
BlockSizeKind.Cells => Number(item.Basis.Value) + "ch",
+ BlockSizeKind.Percent when reserved > 0 && item.Basis.Value > 0 =>
+ "calc(" + Number(item.Basis.Value) + "% - " + Decimal(reserved * item.Basis.Value / 100.0) + "ch)",
BlockSizeKind.Percent => Number(item.Basis.Value) + "%",
_ => item.Min > 1 ? Number(item.Min) + "ch" : "0",
});
diff --git a/MarkupString.Html/LayoutCss.cs b/MarkupString.Html/LayoutCss.cs
index 2354df8..f983a2d 100644
--- a/MarkupString.Html/LayoutCss.cs
+++ b/MarkupString.Html/LayoutCss.cs
@@ -38,7 +38,7 @@ public static class LayoutCss
".ms-border-none.ms-rule::before, .ms-border-none.ms-rule::after { border-top: none; }\n" +
".ms-flex { display: flex; flex-wrap: wrap; }\n" +
".ms-flex.ms-vertical { flex-direction: column; }\n" +
- ".ms-item { min-width: 0; }\n" +
+ ".ms-item { min-width: 0; box-sizing: border-box; }\n" +
".ms-flex.ms-divided > .ms-item + .ms-item { border-left: 1px solid var(--ms-separator, var(--ms-separator-default, currentColor)); padding-left: 1ch; }\n" +
".ms-figure { display: flow-root; }\n" +
".ms-figure-image { max-width: 100%; height: auto; }\n" +
diff --git a/MarkupString.Tests/Layout/BlockLayoutTests.cs b/MarkupString.Tests/Layout/BlockLayoutTests.cs
index 4f2087d..1f6483a 100644
--- a/MarkupString.Tests/Layout/BlockLayoutTests.cs
+++ b/MarkupString.Tests/Layout/BlockLayoutTests.cs
@@ -238,6 +238,22 @@ public async Task Flex_AutoItemsShareTheRest()
await Assert.That(BlockLayout.Lines(flex, 20)[0].ToPlainText()).IsEqualTo("a b c ");
}
+ [Test]
+ public async Task Flex_PercentageItemsLeaveRoomForTheirSeparatorInHtml()
+ {
+ var flex = new Flex(
+ [
+ T("left").Sized(BlockSize.Percent(50)),
+ T("right").Sized(BlockSize.Percent(50)),
+ ])
+ { Separator = P(" │ ") };
+
+ var html = BlockLayout.Build(flex, 40).Render(MarkupFormat.Html, Registry);
+
+ await Assert.That(html).Contains("flex:1 1 calc(50% - 1.5ch)");
+ await Assert.That(LayoutCss.Fixed).Contains(".ms-item { min-width: 0; box-sizing: border-box; }");
+ }
+
[Test]
public async Task Figure_TextFlowsRoundTheArt()
{