docs: add sticky header support for documentation pages

- Add CSS rules to web/static/input.css for header positioning
- Header is sticky only on /docs pages via .page-docs body class
- Add page-docs class to body element in templates/docs.templ
- Non-docs pages have static header position
This commit is contained in:
john-okeefe committed 2026-02-03 16:16:14 -05:00
1 parent d9c6be1429
commit 79373dd225
5 files changed
+38 -524

No files matched your search

+3 -8
View File
@@ -110,6 +110,7 @@ func (h *HTTPHandler) ShowAPIEndpoint(c echo.Context, endpointPath string) error
// Get endpoint data for the explorer
endpointInfo, err := h.docs.GetAPIEndpointData(endpointPath)
_ = endpointInfo
if err != nil {
// Endpoint not found in explorer data, fall back to old behavior
return h.showLegacyAPIEndpoint(c, endpointPath, isLoggedIn)
@@ -168,16 +169,10 @@ func (h *HTTPHandler) ShowAPIEndpoint(c echo.Context, endpointPath string) error
}
}
// Create API explorer data
explorerData := templates.APIExplorerData{
Endpoint: *endpointInfo,
IsLoggedIn: isLoggedIn,
}
// Render API endpoint page with explorer
// Render API endpoint page
var buf bytes.Buffer
currentPath := c.Request().URL.Path
err = templates.DocsLayoutWithExplorer(*nav, *doc, user, explorerData, currentPath).Render(c.Request().Context(), &buf)
err = templates.DocsLayout(*nav, *doc, user, currentPath).Render(c.Request().Context(), &buf)
if err != nil {
return c.HTML(http.StatusInternalServerError, "Failed to render page")
+8 -159
View File
@@ -18,7 +18,7 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
<script src="/static/lunr-flex.min.js"></script>
<script src="/static/header.js"></script>
</head>
<body class={ "theme-" + user.Theme + " bg-background-primary text-text-primary font-sans antialiased" }>
<body class={ "theme-" + user.Theme + " page-docs bg-background-primary text-text-primary font-sans antialiased" }>
@Header(user, currentPath)
<!-- Mobile Menu Button -->
<button
@@ -35,7 +35,7 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
<div id="search-results" class="hidden fixed inset-0 bg-black/95 z-50 overflow-y-auto p-8 transition-opacity duration-200 ease-in-out"></div>
<!-- Sidebar -->
<div class="sidebar fixed left-0 top-0 bottom-0 w-72 bg-background-secondary border-r border-border overflow-y-auto z-40 mt-16 lg:translate-x-0 transition-transform duration-300 ease-in-out">
<div class="sidebar fixed left-0 top-0 bottom-0 w-72 bg-background-secondary border-r border-border overflow-y-auto mt-16 lg:translate-x-0 transition-transform duration-300 ease-in-out">
<!-- Search -->
<div class="p-4 border-b border-border">
<input
@@ -99,8 +99,11 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
<!-- Table of Contents -->
if len(doc.TOC) > 0 {
<div class="toc bg-background-secondary p-4 rounded-lg mb-8 border border-border">
<strong class="block mb-2 text-text-primary font-semibold">On this page</strong>
<details class="toc bg-background-secondary p-4 rounded-lg mb-8 border border-border">
<summary class="cursor-pointer hover:opacity-80 transition-opacity">
<strong class="text-text-primary font-semibold">On this page</strong>
<span class="ml-2 text-text-secondary text-xs">▼</span>
</summary>
for _, item := range doc.TOC {
<a
href={ "#" + item.Anchor }
@@ -110,7 +113,7 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
{ item.Title }
</a>
}
</div>
</details>
}
<!-- Content -->
@@ -143,14 +146,6 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
sidebar.classList.toggle('open');
}
// Close sidebar when clicking main content on mobile
document.querySelector('.main-content')?.addEventListener('click', () => {
const sidebar = document.querySelector('.sidebar');
if (window.innerWidth < 1024) {
sidebar.classList.remove('open');
}
});
// Highlight current page in nav
document.addEventListener('DOMContentLoaded', function() {
const currentPath = window.location.pathname;
@@ -331,159 +326,13 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
}
});
</script>
</body>
</html>
}
templ DocsLayoutWithExplorer(nav Navigation, doc Document, user User, explorer APIExplorerData, currentPath string) {
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{ doc.Title } - Bookhoard Documentation</title>
<link href="/static/style.css" rel="stylesheet">
<link rel="stylesheet" href="/static/highlight-dark.min.css">
<script src="/static/highlight.min.js"></script>
<script src="/static/lunr.min.js"></script>
<script src="/static/lunr-flex.min.js"></script>
<script src="/static/header.js"></script>
</head>
<body class={ "theme-" + user.Theme + " bg-background-primary text-text-primary font-sans antialiased" }>
@Header(user, currentPath)
<!-- Mobile Menu Button -->
<button
class="lg:hidden fixed top-4 left-4 z-50 bg-background-secondary border border-border rounded p-2 text-text-primary hover:bg-background-secondary/80"
onclick="toggleSidebar()"
aria-label="Toggle menu"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<!-- Search Results Overlay -->
<div id="search-results" class="hidden fixed inset-0 bg-black/95 z-50 overflow-y-auto p-4 lg:p-8 transition-opacity duration-200 ease-in-out"></div>
<!-- Sidebar -->
<div class="sidebar fixed left-0 top-0 bottom-0 w-72 bg-background-secondary border-r border-border overflow-y-auto z-40 mt-16 lg:translate-x-0 transition-transform duration-300 ease-in-out">
<!-- Search -->
<div class="p-4 border-b border-border">
<input
type="text"
class="search-input w-full px-3 py-2 rounded bg-background-primary text-text-primary border border-border focus:outline-none focus:ring-2 focus:ring-accent text-sm"
placeholder="Search documentation..."
id="docs-search"
>
</div>
<!-- Navigation Sections -->
for _, section := range nav.Sections {
<div class="nav-section mb-6">
<div
class="nav-section-title font-semibold text-xs uppercase tracking-wider text-text-secondary px-4 py-3 cursor-pointer select-none flex items-center justify-between"
onclick="toggleSection(this)"
>
<span>{ section.Title }</span>
<svg class="w-4 h-4 transform transition-transform section-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</div>
if section.Collapsed {
<div class="nav-items hidden" data-collapsed="true">
for _, item := range section.Items {
<a href={ item.URL } class="nav-item block py-2 px-4 pl-8 text-text-secondary hover:text-accent text-sm transition-colors no-underline">
<span class="mr-2">{ item.Icon }</span>{ item.Title }
</a>
}
</div>
} else {
<div class="nav-items" data-collapsed="false">
for _, item := range section.Items {
<a href={ item.URL } class="nav-item block py-2 px-4 pl-8 text-text-secondary hover:text-accent text-sm transition-colors no-underline">
<span class="mr-2">{ item.Icon }</span>{ item.Title }
</a>
}
</div>
}
</div>
}
</div>
<!-- Main Content -->
<main class="main-content lg:ml-72 p-4 lg:p-8 max-w-4xl mx-auto">
<!-- Breadcrumb -->
if len(doc.Breadcrumb) > 0 {
<nav class="breadcrumb flex flex-wrap gap-2 text-sm text-text-secondary mb-8" aria-label="Breadcrumb">
for i, crumb := range doc.Breadcrumb {
if i > 0 {
<span class="text-text-secondary/50">›</span>
}
<a href={ crumb.URL } class="text-accent hover:underline no-underline">{ crumb.Title }</a>
}
</nav>
}
<!-- Title -->
<h1 class="text-3xl lg:text-4xl font-bold mb-6 lg:mb-8 text-text-primary">{ doc.Title }</h1>
<!-- Table of Contents -->
if len(doc.TOC) > 0 {
<div class="toc bg-background-secondary p-4 rounded-lg mb-6 lg:mb-8 border border-border lg:static">
<strong class="block mb-2 text-text-primary font-semibold">On this page</strong>
for _, item := range doc.TOC {
<a
href={ "#" + item.Anchor }
class="toc-item block py-1 text-text-secondary hover:text-accent text-sm no-underline"
style={ "margin-left: " + fmt.Sprintf("%drem", item.Level) }
>
{ item.Title }
</a>
}
</div>
}
<!-- Content -->
<div class="prose prose-invert prose-sm lg:prose-base max-w-none">
@UnsafeHTML(doc.Content).ToComponent()
</div>
<!-- API Explorer -->
@APIExplorer(explorer)
</main>
<script>
// Toggle sidebar section
function toggleSection(el) {
const items = el.nextElementSibling;
const arrow = el.querySelector('.section-arrow');
const isCollapsed = items.getAttribute('data-collapsed') === 'true';
if (isCollapsed) {
items.classList.remove('hidden');
items.setAttribute('data-collapsed', 'false');
arrow.style.transform = 'rotate(0deg)';
} else {
items.classList.add('hidden');
items.setAttribute('data-collapsed', 'true');
arrow.style.transform = 'rotate(-90deg)';
}
}
// Toggle sidebar on mobile
function toggleSidebar() {
const sidebar = document.querySelector('.sidebar');
sidebar.classList.toggle('open');
}
// Close sidebar when clicking main content on mobile
document.querySelector('.main-content')?.addEventListener('click', () => {
const sidebar = document.querySelector('.sidebar');
if (window.innerWidth < 1024) {
sidebar.classList.remove('open');
}
});
// Highlight current page in nav
document.addEventListener('DOMContentLoaded', function() {
+8 -356
View File
@@ -50,7 +50,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 = []any{"theme-" + user.Theme + " bg-background-primary text-text-primary font-sans antialiased"}
var templ_7745c5c3_Var3 = []any{"theme-" + user.Theme + " page-docs bg-background-primary text-text-primary font-sans antialiased"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
@@ -76,7 +76,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<!-- Mobile Menu Button --><button class=\"lg:hidden fixed top-4 left-4 z-50 bg-background-secondary border border-border rounded p-2 text-text-primary hover:bg-background-secondary/80\" onclick=\"toggleSidebar()\" aria-label=\"Toggle menu\"><svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path></svg></button><!-- Search Results Overlay --><div id=\"search-results\" class=\"hidden fixed inset-0 bg-black/95 z-50 overflow-y-auto p-8 transition-opacity duration-200 ease-in-out\"></div><!-- Sidebar --><div class=\"sidebar fixed left-0 top-0 bottom-0 w-72 bg-background-secondary border-r border-border overflow-y-auto z-40 mt-16 lg:translate-x-0 transition-transform duration-300 ease-in-out\"><!-- Search --><div class=\"p-4 border-b border-border\"><input type=\"text\" class=\"search-input w-full px-3 py-2 rounded bg-background-primary text-text-primary border border-border focus:outline-none focus:ring-2 focus:ring-accent text-sm\" placeholder=\"Search documentation...\" id=\"docs-search\"></div><!-- Navigation Sections -->")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<!-- Mobile Menu Button --><button class=\"lg:hidden fixed top-4 left-4 z-50 bg-background-secondary border border-border rounded p-2 text-text-primary hover:bg-background-secondary/80\" onclick=\"toggleSidebar()\" aria-label=\"Toggle menu\"><svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path></svg></button><!-- Search Results Overlay --><div id=\"search-results\" class=\"hidden fixed inset-0 bg-black/95 z-50 overflow-y-auto p-8 transition-opacity duration-200 ease-in-out\"></div><!-- Sidebar --><div class=\"sidebar fixed left-0 top-0 bottom-0 w-72 bg-background-secondary border-r border-border overflow-y-auto mt-16 lg:translate-x-0 transition-transform duration-300 ease-in-out\"><!-- Search --><div class=\"p-4 border-b border-border\"><input type=\"text\" class=\"search-input w-full px-3 py-2 rounded bg-background-primary text-text-primary border border-border focus:outline-none focus:ring-2 focus:ring-accent text-sm\" placeholder=\"Search documentation...\" id=\"docs-search\"></div><!-- Navigation Sections -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -282,7 +282,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
return templ_7745c5c3_Err
}
if len(doc.TOC) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<div class=\"toc bg-background-secondary p-4 rounded-lg mb-8 border border-border\"><strong class=\"block mb-2 text-text-primary font-semibold\">On this page</strong> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<details class=\"toc bg-background-secondary p-4 rounded-lg mb-8 border border-border\"><summary class=\"cursor-pointer hover:opacity-80 transition-opacity\"><strong class=\"text-text-primary font-semibold\">On this page</strong> <span class=\"ml-2 text-text-secondary text-xs\">▼</span></summary> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -294,7 +294,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
var templ_7745c5c3_Var15 templ.SafeURL
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinURLErrs("#" + item.Anchor)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 106, Col: 32}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 109, Col: 32}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
@@ -307,7 +307,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("margin-left: " + fmt.Sprintf("%drem", item.Level))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 108, Col: 66}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 111, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
@@ -320,7 +320,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 110, Col: 20}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 113, Col: 20}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
@@ -331,7 +331,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</details>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -344,355 +344,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</div></main><script>\n\t\t\t\t// Toggle sidebar section\n\t\t\t\tfunction toggleSection(el) {\n\t\t\t\t\tconst items = el.nextElementSibling;\n\t\t\t\t\tconst arrow = el.querySelector('.section-arrow');\n\t\t\t\t\tconst isCollapsed = items.getAttribute('data-collapsed') === 'true';\n\n\t\t\t\t\tif (isCollapsed) {\n\t\t\t\t\t\titems.classList.remove('hidden');\n\t\t\t\t\t\titems.setAttribute('data-collapsed', 'false');\n\t\t\t\t\t\tarrow.style.transform = 'rotate(0deg)';\n\t\t\t\t\t} else {\n\t\t\t\t\t\titems.classList.add('hidden');\n\t\t\t\t\t\titems.setAttribute('data-collapsed', 'true');\n\t\t\t\t\t\tarrow.style.transform = 'rotate(-90deg)';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Toggle sidebar on mobile\n\t\t\t\tfunction toggleSidebar() {\n\t\t\t\t\tconst sidebar = document.querySelector('.sidebar');\n\t\t\t\t\tsidebar.classList.toggle('open');\n\t\t\t\t}\n\n\t\t\t\t// Close sidebar when clicking main content on mobile\n\t\t\t\tdocument.querySelector('.main-content')?.addEventListener('click', () => {\n\t\t\t\t\tconst sidebar = document.querySelector('.sidebar');\n\t\t\t\t\tif (window.innerWidth < 1024) {\n\t\t\t\t\t\tsidebar.classList.remove('open');\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// Highlight current page in nav\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst currentPath = window.location.pathname;\n\t\t\t\t\tdocument.querySelectorAll('.nav-item').forEach(item => {\n\t\t\t\t\t\tif (item.getAttribute('href') === currentPath) {\n\t\t\t\t\t\t\titem.classList.add('text-accent', 'bg-accent/10', 'border-r-2', 'border-accent');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\t// Initialize syntax highlighting\n\t\t\t\t\tif (typeof hljs !== 'undefined') {\n\t\t\t\t\t\thljs.highlightAll();\n\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\tLine truncated
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func DocsLayoutWithExplorer(nav Navigation, doc Document, user User, explorer APIExplorerData, currentPath string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var18 := templ.GetChildren(ctx)
if templ_7745c5c3_Var18 == nil {
templ_7745c5c3_Var18 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<!doctype html><html lang=\"en\" class=\"dark\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(doc.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 344, Col: 21}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, " - Bookhoard Documentation</title><link href=\"/static/style.css\" rel=\"stylesheet\"><link rel=\"stylesheet\" href=\"/static/highlight-dark.min.css\"><script src=\"/static/highlight.min.js\"></script><script src=\"/static/lunr.min.js\"></script><script src=\"/static/lunr-flex.min.js\"></script><script src=\"/static/header.js\"></script></head>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 = []any{"theme-" + user.Theme + " bg-background-primary text-text-primary font-sans antialiased"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var20...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<body class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var20).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Header(user, currentPath).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<!-- Mobile Menu Button --><button class=\"lg:hidden fixed top-4 left-4 z-50 bg-background-secondary border border-border rounded p-2 text-text-primary hover:bg-background-secondary/80\" onclick=\"toggleSidebar()\" aria-label=\"Toggle menu\"><svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path></svg></button><!-- Search Results Overlay --><div id=\"search-results\" class=\"hidden fixed inset-0 bg-black/95 z-50 overflow-y-auto p-4 lg:p-8 transition-opacity duration-200 ease-in-out\"></div><!-- Sidebar --><div class=\"sidebar fixed left-0 top-0 bottom-0 w-72 bg-background-secondary border-r border-border overflow-y-auto z-40 mt-16 lg:translate-x-0 transition-transform duration-300 ease-in-out\"><!-- Search --><div class=\"p-4 border-b border-border\"><input type=\"text\" class=\"search-input w-full px-3 py-2 rounded bg-background-primary text-text-primary border border-border focus:outline-none focus:ring-2 focus:ring-accent text-sm\" placeholder=\"Search documentation...\" id=\"docs-search\"></div><!-- Navigation Sections -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, section := range nav.Sections {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<div class=\"nav-section mb-6\"><div class=\"nav-section-title font-semibold text-xs uppercase tracking-wider text-text-secondary px-4 py-3 cursor-pointer select-none flex items-center justify-between\" onclick=\"toggleSection(this)\"><span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(section.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 388, Col: 28}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</span> <svg class=\"w-4 h-4 transform transition-transform section-arrow\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path></svg></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if section.Collapsed {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<div class=\"nav-items hidden\" data-collapsed=\"true\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range section.Items {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var23 templ.SafeURL
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinURLErrs(item.URL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 396, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\" class=\"nav-item block py-2 px-4 pl-8 text-text-secondary hover:text-accent text-sm transition-colors no-underline\"><span class=\"mr-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(item.Icon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 397, Col: 40}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 397, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<div class=\"nav-items\" data-collapsed=\"false\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range section.Items {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var26 templ.SafeURL
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinURLErrs(item.URL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 404, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\" class=\"nav-item block py-2 px-4 pl-8 text-text-secondary hover:text-accent text-sm transition-colors no-underline\"><span class=\"mr-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var27 string
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(item.Icon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 405, Col: 40}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var28 string
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 405, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</div><!-- Main Content --><main class=\"main-content lg:ml-72 p-4 lg:p-8 max-w-4xl mx-auto\"><!-- Breadcrumb -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(doc.Breadcrumb) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<nav class=\"breadcrumb flex flex-wrap gap-2 text-sm text-text-secondary mb-8\" aria-label=\"Breadcrumb\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for i, crumb := range doc.Breadcrumb {
if i > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<span class=\"text-text-secondary/50\">›</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, " <a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var29 templ.SafeURL
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinURLErrs(crumb.URL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 423, Col: 26}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\" class=\"text-accent hover:underline no-underline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var30 string
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(crumb.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 423, Col: 91}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<!-- Title --><h1 class=\"text-3xl lg:text-4xl font-bold mb-6 lg:mb-8 text-text-primary\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var31 string
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(doc.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 429, Col: 89}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "</h1><!-- Table of Contents -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(doc.TOC) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<div class=\"toc bg-background-secondary p-4 rounded-lg mb-6 lg:mb-8 border border-border lg:static\"><strong class=\"block mb-2 text-text-primary font-semibold\">On this page</strong> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range doc.TOC {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var32 templ.SafeURL
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs("#" + item.Anchor)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 437, Col: 32}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "\" class=\"toc-item block py-1 text-text-secondary hover:text-accent text-sm no-underline\" style=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var33 string
templ_7745c5c3_Var33, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("margin-left: " + fmt.Sprintf("%drem", item.Level))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 439, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var34 string
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 441, Col: 20}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<!-- Content --><div class=\"prose prose-invert prose-sm lg:prose-base max-w-none\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = UnsafeHTML(doc.Content).ToComponent().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</div><!-- API Explorer -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = APIExplorer(explorer).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "</main><script>\n\t\t\t\t// Toggle sidebar section\n\t\t\t\tfunction toggleSection(el) {\n\t\t\t\t\tconst items = el.nextElementSibling;\n\t\t\t\t\tconst arrow = el.querySelector('.section-arrow');\n\t\t\t\t\tconst isCollapsed = items.getAttribute('data-collapsed') === 'true';\n\n\t\t\t\t\tif (isCollapsed) {\n\t\t\t\t\t\titems.classList.remove('hidden');\n\t\t\t\t\t\titems.setAttribute('data-collapsed', 'false');\n\t\t\t\t\t\tarrow.style.transform = 'rotate(0deg)';\n\t\t\t\t\t} else {\n\t\t\t\t\t\titems.classList.add('hidden');\n\t\t\t\t\t\titems.setAttribute('data-collapsed', 'true');\n\t\t\t\t\t\tarrow.style.transform = 'rotate(-90deg)';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Toggle sidebar on mobile\n\t\t\t\tfunction toggleSidebar() {\n\t\t\t\t\tconst sidebar = document.querySelector('.sidebar');\n\t\t\t\t\tsidebar.classList.toggle('open');\n\t\t\t\t}\n\n\t\t\t\t// Close sidebar when clicking main content on mobile\n\t\t\t\tdocument.querySelector('.main-content')?.addEventListener('click', () => {\n\t\t\t\t\tconst sidebar = document.querySelector('.sidebar');\n\t\t\t\t\tif (window.innerWidth < 1024) {\n\t\t\t\t\t\tsidebar.classList.remove('open');\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// Highlight current page in nav\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst currentPath = window.location.pathname;\n\t\t\t\t\tdocument.querySelectorAll('.nav-item').forEach(item => {\n\t\t\t\t\t\tif (item.getAttribute('href') === currentPath) {\n\t\t\t\t\t\t\titem.classList.add('text-accent', 'bg-accent/10', 'border-r-2', 'border-accent');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\t// Initialize syntax highlighting\n\t\t\t\t\tif (typeof hljs !== 'undefined') {\n\t\t\t\t\t\thljs.highlightAll();\n\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\t\t\t\tLine truncated
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</div></main><script>\n\t\t\t\t// Toggle sidebar section\n\t\t\t\tfunction toggleSection(el) {\n\t\t\t\t\tconst items = el.nextElementSibling;\n\t\t\t\t\tconst arrow = el.querySelector('.section-arrow');\n\t\t\t\t\tconst isCollapsed = items.getAttribute('data-collapsed') === 'true';\n\n\t\t\t\t\tif (isCollapsed) {\n\t\t\t\t\t\titems.classList.remove('hidden');\n\t\t\t\t\t\titems.setAttribute('data-collapsed', 'false');\n\t\t\t\t\t\tarrow.style.transform = 'rotate(0deg)';\n\t\t\t\t\t} else {\n\t\t\t\t\t\titems.classList.add('hidden');\n\t\t\t\t\t\titems.setAttribute('data-collapsed', 'true');\n\t\t\t\t\t\tarrow.style.transform = 'rotate(-90deg)';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Toggle sidebar on mobile\n\t\t\t\tfunction toggleSidebar() {\n\t\t\t\t\tconst sidebar = document.querySelector('.sidebar');\n\t\t\t\t\tsidebar.classList.toggle('open');\n\t\t\t\t}\n\n\t\t\t\t// Highlight current page in nav\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst currentPath = window.location.pathname;\n\t\t\t\t\tdocument.querySelectorAll('.nav-item').forEach(item => {\n\t\t\t\t\t\tif (item.getAttribute('href') === currentPath) {\n\t\t\t\t\t\t\titem.classList.add('text-accent', 'bg-accent/10', 'border-r-2', 'border-accent');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\t// Initialize syntax highlighting\n\t\t\t\t\tif (typeof hljs !== 'undefined') {\n\t\t\t\t\t\thljs.highlightAll();\n\t\t\t\t\t\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\t\t// Add copy buttons to all code blocks\n\t\t\t\t\tdocument.querySelectorAll('pre code').forEach((block) => {\n\t\t\t\t\t\tconst pre = block.parentElement;\n\t\t\t\t\t\tconst wrapper = document.createElement('div');\n\t\t\t\t\t\twrapper.className = 'relative group';\n\t\t\t\t\t\tpre.parentNode.insertBefore(wrapper, pre);\n\t\t\t\t\t\twrapper.appendChild(pre);\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst button = document.createElement('button');\n\t\t\t\t\t\tbutton.className = 'absolute top-2 right-2 bg-background-secondary text-text-secondary px-2 py-1 rounded text-xs opacity-0 group-hover:opacity-100 transition-opacity border border-border hover:text-accent';\n\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\tbutton.onclick = async () => {\n\t\t\t\t\t\t\tawait navigator.clipboard.writeText(block.textContent);\n\t\t\t\t\t\t\tbutton.textContent = 'Copied!';\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tbutton.textContent = 'Copy';\n\t\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t\t};\n\t\t\t\t\t\twrapper.appendChild(button);\n\t\t\t\t\t});\n\n\t\t\t\t// Search state\n\t\t\t\tlet idx;\n\t\t\t\tlet searchDocs = [];\n\n\t\t\t\t// Load index on page load\n\t\t\t\tfetch('/docs/search-index.json')\n\t\t\t\t\t.then(r => r.json())\n\t\t\t\t\t.then(data => {\n\t\t\t\t\t\tidx = lunr(function() {\n\t\t\t\t\t\t\tthis.use(lunr.flex);\n\t\t\t\t\t\t\tthisLine truncated
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
+18
View File
@@ -144,6 +144,24 @@
color: var(--text-primary);
}
/* Header component styles */
.header-nav {
position: sticky;
top: 0;
}
/* Make header sticky only on docs pages */
body:not(.page-docs) .header-nav {
position: static;
}
/* Add class to body on docs pages */
.page-docs .header-nav {
position: sticky;
top: 0;
z-index: 50;
}
.card {
background-color: var(--bg-secondary);
border-color: var(--border);
+1 -1
View File
@@ -1,2 +1,2 @@
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }
/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}input:where(:not([type])),input:where([type=date]),input:where([type=datetime-local]),input:where([type=email]),input:where([type=month]),input:where([type=number]),input:where([type=password]),input:where([type=search]),input:where([type=tel]),input:where([type=text]),input:where([type=time]),input:where([type=url]),input:where([type=week]),select,select:where([multiple]),textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}input:where(:not([type])):focus,input:where([type=date]):focus,input:where([type=datetime-local]):focus,input:where([type=email]):focus,input:where([type=month]):focus,input:where([type=number]):focus,input:where([type=password]):focus,input:where([type=search]):focus,input:where([type=tel]):focus,input:where([type=text]):focus,input:where([type=time]):focus,input:where([type=url]):focus,input:where([type=week]):focus,select:focus,select:where([multiple]):focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3Line truncated
/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}input:where(:not([type])),input:where([type=date]),input:where([type=datetime-local]),input:where([type=email]),input:where([type=month]),input:where([type=number]),input:where([type=password]),input:where([type=search]),input:where([type=tel]),input:where([type=text]),input:where([type=time]),input:where([type=url]),input:where([type=week]),select,select:where([multiple]),textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}input:where(:not([type])):focus,input:where([type=date]):focus,input:where([type=datetime-local]):focus,input:where([type=email]):focus,input:where([type=month]):focus,input:where([type=number]):focus,input:where([type=password]):focus,input:where([type=search]):focus,input:where([type=tel]):focus,input:where([type=text]):focus,input:where([type=time]):focus,input:where([type=url]):focus,input:where([type=week]):focus,select:focus,select:where([multiple]):focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3Line truncated