Skip to content

Commit 13dbe91

Browse files
Mpdreamzclaude
andauthored
perf(nav): reduce HTML output size ~34% by extracting repeated nav markup (#3435)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent eda7f35 commit 13dbe91

4 files changed

Lines changed: 94 additions & 54 deletions

File tree

src/Elastic.Documentation.Site/Assets/styles.css

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,72 @@ body {
125125
}
126126
}
127127

128+
.nav-badge {
129+
@apply ml-auto inline-flex shrink-0 items-center rounded px-1.5 py-0.5 text-[9px] font-bold;
130+
}
131+
132+
.nav-badge-ns {
133+
background: #f5f3ff;
134+
color: #7c3aed;
135+
border: 1px solid #ddd6fe;
136+
}
137+
138+
.nav-badge-cmd {
139+
background: #fffbeb;
140+
color: #b45309;
141+
border: 1px solid #fde68a;
142+
}
143+
144+
.nav-badge-alias {
145+
background: #f0f9ff;
146+
color: #0369a1;
147+
border: 1px solid #bae6fd;
148+
}
149+
150+
.nav-link {
151+
@apply group-[.current]/li:text-blue-elastic! flex w-full grow items-center;
152+
}
153+
154+
.nav-folder-link {
155+
@apply group-[.current]/li:text-blue-elastic! flex content-center items-center gap-1 pr-2;
156+
}
157+
158+
.nav-folder {
159+
@apply relative flex flex-wrap;
160+
}
161+
162+
.nav-toggle-btn {
163+
@apply hover:bg-grey-20 cursor-pointer rounded-sm p-1;
164+
}
165+
166+
.nav-chevron {
167+
@apply w-3.5 shrink-0 -rotate-90 group-has-checked/label:rotate-0;
168+
fill: none;
169+
stroke-width: 1.5;
170+
stroke: currentColor;
171+
}
172+
173+
.nav-subtree {
174+
@apply relative ml-4 hidden w-full;
175+
176+
/* revealed when the sibling .peer div contains a checked checkbox */
177+
.peer:has(:checked) ~ & {
178+
display: block;
179+
}
180+
181+
&::before {
182+
content: '';
183+
@apply bg-grey-20 absolute top-0 bottom-0 w-px;
184+
left: calc(var(--spacing) * -4 + 1px);
185+
}
186+
187+
/* darken connector line when subtree contains the current page */
188+
&:has(.current)::before,
189+
.peer:has(.current) ~ &::before {
190+
@apply bg-grey-40;
191+
}
192+
}
193+
128194
.sidebar-trial-card {
129195
background: var(--color-green-30);
130196
border-radius: 8px;

src/Elastic.Documentation.Site/Navigation/_TocTree.cshtml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@
3838
</div>
3939
</button>
4040
<div class="hidden group-focus-within:block left-0 right-0 absolute top-full">
41-
<ul class="mt-1 py-2 bg-white border-1 border-grey-20 rounded-sm shadow-md">
41+
<ul class="mt-1 py-2 bg-white border-1 border-grey-20 rounded-sm shadow-md"
42+
@Model.Htmx.GetNavHxAttributes(false, "mouseover")>
4243
@foreach (var item in Model.TopLevelItems)
4344
{
4445
<li class="block">
4546
<a
4647
class="block py-2 px-4 hover:underline hover:text-black hover:bg-grey-10 active:bg-blue-elastic-70 active:text-white font-semibold @(item.NavigationRoot.Id == Model.Tree.Id ? "text-blue-elastic" : "")"
47-
href="@item.Url"
48-
@Model.Htmx.GetNavHxAttributes(false, "mouseover")>
48+
href="@item.Url">
4949
@item.NavigationTitle
5050
</a>
5151
</li>
@@ -62,7 +62,7 @@
6262
</div>
6363
</div>
6464

65-
<ul class="block px-4" id="nav-tree">
65+
<ul class="block px-4" id="nav-tree" @Model.Htmx.GetNavHxAttributes(true)>
6666
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
6767
{
6868
IsPrimaryNavEnabled = Model.IsPrimaryNavEnabled,

src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,12 @@
1414
if (raw.StartsWith("[alias]", StringComparison.Ordinal)) return ("alias", raw[7..]);
1515
return (null, raw);
1616
}
17-
18-
// Inline styles to avoid Tailwind purge — very muted tints, subtle border for definition
19-
const string NsStyle = "background:#f5f3ff;color:#7c3aed;border:1px solid #ddd6fe;";
20-
const string CmdStyle = "background:#fffbeb;color:#b45309;border:1px solid #fde68a;";
21-
const string AliasStyle = "background:#f0f9ff;color:#0369a1;border:1px solid #bae6fd;";
22-
23-
static string BadgeStyle(string? badge) => badge switch { "ns" => NsStyle, "cmd" => CmdStyle, "alias" => AliasStyle, _ => "" };
2417
}
2518
@if (isTopLevel && !Model.IsGlobalAssemblyBuild && !Model.IsPrimaryNavEnabled && !Model.SubTree.Index.Hidden)
2619
{
2720
var idx = Model.SubTree.Index;
2821
<li class="flex group/li pr-8 mt-6">
29-
<a
30-
href="@idx.Url"
31-
@Model.Htmx.GetNavHxAttributes(true)
32-
class="sidebar-link grow group-[.current]/li:text-blue-elastic!"
33-
>
34-
@idx.NavigationTitle
35-
</a>
22+
<a href="@idx.Url" class="sidebar-link nav-link">@idx.NavigationTitle</a>
3623
</li>
3724
}
3825
@foreach (var item in Model.SubTree.NavigationItems)
@@ -50,15 +37,11 @@
5037
{
5138
var (groupBadge, groupLabel) = ParseNavTitle(group.NavigationTitle);
5239
<li class="flex group/li pr-8 @(isTopLevel ? "font-semibold mt-6" : "mt-4")">
53-
<a
54-
href="@group.Url"
55-
@Model.Htmx.GetNavHxAttributes(true)
56-
class="sidebar-link group-[.current]/li:text-blue-elastic! flex items-center w-full"
57-
>
40+
<a href="@group.Url" class="sidebar-link nav-link">
5841
<span>@groupLabel</span>
59-
@if (groupBadge == "ns") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@NsStyle">ns</span> }
60-
else if (groupBadge == "cmd") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@CmdStyle">cmd</span> }
61-
else if (groupBadge == "alias") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@AliasStyle">alias</span> }
42+
@if (groupBadge == "ns") { <span class="nav-badge nav-badge-ns">ns</span> }
43+
else if (groupBadge == "cmd") { <span class="nav-badge nav-badge-cmd">cmd</span> }
44+
else if (groupBadge == "alias") { <span class="nav-badge nav-badge-alias">alias</span> }
6245
</a>
6346
</li>
6447
}
@@ -67,30 +50,19 @@
6750
var g = folder;
6851
var allHidden = folder.NavigationItems.All(n => n.Hidden);
6952
var (folderBadge, folderLabel) = ParseNavTitle(g.NavigationTitle);
70-
<li class="flex flex-wrap group-navigation @(isTopLevel ? "mt-6" : "mt-4") relative">
53+
<li class="nav-folder @(isTopLevel ? "mt-6" : "mt-4")">
7154
<div class="peer grid grid-cols-[1fr_auto] w-full">
72-
<a
73-
href="@(g.Url)"
74-
@Model.Htmx.GetNavHxAttributes(true)
75-
class="sidebar-link pr-2 content-center @(isTopLevel ? "font-semibold" : "") group-[.current]/li:text-blue-elastic! flex items-center gap-1">
55+
<a href="@(g.Url)" class="sidebar-link nav-folder-link@(isTopLevel ? " font-semibold" : "")">
7656
<span>@folderLabel</span>
77-
@if (folderBadge == "ns") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@NsStyle">ns</span> }
78-
else if (folderBadge == "cmd") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@CmdStyle">cmd</span> }
79-
else if (folderBadge == "alias") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@AliasStyle">alias</span> }
57+
@if (folderBadge == "ns") { <span class="nav-badge nav-badge-ns">ns</span> }
58+
else if (folderBadge == "cmd") { <span class="nav-badge nav-badge-cmd">cmd</span> }
59+
else if (folderBadge == "alias") { <span class="nav-badge nav-badge-alias">alias</span> }
8060
</a>
8161
@if (!allHidden)
8262
{
8363
<label for="@folder.Id" class="group/label flex mr-2 items-start">
84-
<div class="hover:bg-grey-20 rounded-sm p-1 cursor-pointer">
85-
<svg
86-
xmlns="http://www.w3.org/2000/svg"
87-
fill="none"
88-
viewBox="0 0 24 24"
89-
stroke-width="1.5"
90-
stroke="currentColor"
91-
class="shrink-0 -rotate-90 group-has-checked/label:rotate-0 w-3.5">
92-
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>
93-
</svg>
64+
<div class="nav-toggle-btn">
65+
<svg class="nav-chevron" viewBox="0 0 24 24"><use href="#icon-chevron-down"/></svg>
9466
</div>
9567
<input
9668
id="@folder.Id"
@@ -102,7 +74,7 @@
10274
</div>
10375
@if (g.NavigationItems.Count > 0)
10476
{
105-
<ul class="w-full hidden peer-has-checked:block ml-4 relative before:content-[''] before:absolute before:-left-[calc(var(--spacing)*4-1px)] before:top-0 before:bottom-0 before:w-px before:bg-grey-20 before:peer-has-checked:block peer-has-[.current]:before:bg-grey-40 has-[.current]:before:bg-grey-40">
77+
<ul class="nav-subtree">
10678
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
10779
{
10880
IsPrimaryNavEnabled = Model.IsPrimaryNavEnabled,
@@ -118,18 +90,13 @@
11890
}
11991
else if (item is ILeafNavigationItem<INavigationModel> leaf)
12092
{
121-
var hasSameTopLevelGroup = true;
12293
var (leafBadge, leafLabel) = ParseNavTitle(leaf.NavigationTitle);
12394
<li class="flex group/li pr-8 @(isTopLevel ? "font-semibold mt-6" : "mt-4")">
124-
<a
125-
href="@leaf.Url"
126-
@Model.Htmx.GetNavHxAttributes(hasSameTopLevelGroup)
127-
class="sidebar-link grow group-[.current]/li:text-blue-elastic! flex items-center w-full"
128-
>
95+
<a href="@leaf.Url" class="sidebar-link nav-link">
12996
<span>@leafLabel</span>
130-
@if (leafBadge == "ns") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@NsStyle">ns</span> }
131-
else if (leafBadge == "cmd") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@CmdStyle">cmd</span> }
132-
else if (leafBadge == "alias") { <span class="ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold shrink-0" style="@AliasStyle">alias</span> }
97+
@if (leafBadge == "ns") { <span class="nav-badge nav-badge-ns">ns</span> }
98+
else if (leafBadge == "cmd") { <span class="nav-badge nav-badge-cmd">cmd</span> }
99+
else if (leafBadge == "alias") { <span class="nav-badge nav-badge-alias">alias</span> }
133100
</a>
134101
</li>
135102
}

src/Elastic.Documentation.Site/_GlobalLayout.cshtml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
data-root-path="@(Model.Htmx.GetRootPath())"
1616
hx-boost="true" hx-swap="none" hx-push-url="true" hx-indicator="#htmx-indicator"
1717
>
18+
<svg xmlns="http://www.w3.org/2000/svg" class="hidden" aria-hidden="true">
19+
<defs>
20+
<symbol id="icon-chevron-down" viewBox="0 0 24 24">
21+
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>
22+
</symbol>
23+
</defs>
24+
</svg>
1825
@if (Model.GoogleTagManager.Enabled)
1926
{
2027
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=@(Model.GoogleTagManager.Id)@(new HtmlString(Model.GoogleTagManager.QueryString()))"

0 commit comments

Comments
 (0)