mirror of
https://github.com/docker/docs.git
synced 2026-11-04 10:01:00 +00:00
## What A full revamp of the `/guides` section — taxonomy, structure, and UX. Preview: https://deploy-preview-25338--docsdocker.netlify.app/guides/ ### Taxonomy & structure - Replaces the sprawling 24-tag set with a focused 9-tag taxonomy aligned to use case / SDLC: **Languages & frameworks, AI, Testing, CI/CD, Security, Databases, Deployment, Admin, Hands-on labs**. - Removes the languages taxonomy entirely (`data/languages.yaml` and all `languages:` front matter). - Collapses all multi-page guide series into single pages, with aliases so old sub-page URLs continue to resolve. - Deletes redirect-only shim pages and flattens directory-wrapped single pages. ### Landing page (`/guides/`) - A clean index: hero, then category sections listing each guide as a plain **title / summary** row. - **Browse rail** (sticky, left): click a topic to filter the page to just that topic's guides; "All guides" resets. A count + Clear bar appears while filtering. - **Text filter**: instant client-side search over title, summary, and tags. Topic filter and text search are mutually exclusive and share the same flat-list view. - No more "Featured guides" section. ### Guide content pages - Removes the left sidebar for the guides section (mobile drawer kept). - Centered reading column (`92ch`) with the TOC taking the remaining width. - Summary rendered as a lead paragraph under the h1. - TOC scrollbar auto-hides until hover; breadcrumbs render at a consistent size inside and outside `prose`. ## Notes - Deep full-text search across guide bodies is still handled by the existing site-wide Pagefind (⌘K); the landing filter is the fast metadata-narrowing surface. - `series.html` is retained — it's still used by `get-started` pages. ## Validation - Site builds clean locally (Hugo dev server, no errors / broken refs). - All collapsed sub-page URLs redirect via aliases. ## Follow-ups - Prune guides that we no longer need/want - Tighten the guides that span too many concepts (e.g. language guides; remove cicd/deploy/sscs) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
219 lines
9.8 KiB
HTML
219 lines
9.8 KiB
HTML
{{ define "left" }}
|
|
{{- partial "sidebar/mainnav.html" . }}
|
|
{{ end }}
|
|
|
|
{{ define "main" }}
|
|
{{- $tagOrder := slice "languages" "ai" "testing" "cicd" "security" "databases" "deployment" "admin" "labs" }}
|
|
|
|
<div
|
|
class="not-prose mx-auto flex min-h-[calc(100dvh-7rem)] max-w-7xl flex-col"
|
|
x-data="{
|
|
query: '',
|
|
tagFilter: '',
|
|
words() { return this.query.trim().toLowerCase().split(/\s+/).filter(Boolean) },
|
|
textFiltering() { return this.query.trim().length > 0 },
|
|
filtering() { return this.textFiltering() || this.tagFilter !== '' },
|
|
matchText(text) { return this.words().every(w => text.includes(w)) },
|
|
rowVisible(el) { return !this.textFiltering() || this.matchText(el.dataset.search) },
|
|
sectionVisible(el) {
|
|
if (this.tagFilter) return el.dataset.tag === this.tagFilter;
|
|
if (this.textFiltering()) return Array.from(el.querySelectorAll('[data-guide]')).some(r => this.matchText(r.dataset.search));
|
|
return true;
|
|
},
|
|
resultCount() {
|
|
if (this.tagFilter) return document.querySelectorAll('section[data-tag=' + this.tagFilter + '] [data-guide]').length;
|
|
return Array.from(document.querySelectorAll('[data-guide]')).filter(r => this.matchText(r.dataset.search)).length;
|
|
},
|
|
selectTag(tag) { this.tagFilter = this.tagFilter === tag ? '' : tag; this.query = ''; window.scrollTo({ top: 0, behavior: 'smooth' }); },
|
|
clear() { this.query = ''; this.tagFilter = ''; }
|
|
}"
|
|
>
|
|
{{- partial "breadcrumbs.html" . }}
|
|
|
|
<div class="mt-4">
|
|
<h1
|
|
data-pagefind-weight="10"
|
|
class="scroll-mt-36 text-5xl font-bold tracking-tight"
|
|
>
|
|
Guides
|
|
</h1>
|
|
{{- with .Description }}
|
|
<p class="mt-3 text-lg text-gray-500 dark:text-gray-400">{{ . }}</p>
|
|
{{- end }}
|
|
</div>
|
|
|
|
<!-- Text filter -->
|
|
<div class="relative mt-8 mb-10 max-w-xl">
|
|
<span
|
|
class="icon-svg icon-sm pointer-events-none absolute top-1/2 left-3.5 -translate-y-1/2 text-gray-400 dark:text-gray-500"
|
|
>
|
|
{{ partialCached "icon" "magnifying-glass" "magnifying-glass" }}
|
|
</span>
|
|
<input
|
|
type="search"
|
|
x-model="query"
|
|
@input="tagFilter = ''"
|
|
@keydown.escape="clear()"
|
|
aria-label="Filter guides"
|
|
placeholder="Filter guides by name, topic, or tag…"
|
|
class="w-full rounded-xl border border-gray-300 bg-white py-2.5 pr-10 pl-11 text-sm text-gray-900 shadow-sm transition placeholder:text-gray-400 focus:border-blue-400 focus:ring-2 focus:ring-blue-400/40 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100 dark:placeholder:text-gray-500"
|
|
/>
|
|
<button
|
|
x-show="query"
|
|
x-cloak
|
|
@click="clear()"
|
|
aria-label="Clear filter"
|
|
class="icon-svg icon-sm absolute top-1/2 right-2.5 -translate-y-1/2 rounded-md p-1 text-gray-400 transition hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
|
>
|
|
{{ partialCached "icon" "x-mark" "x-mark" }}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="flex flex-1 gap-16 xl:gap-24">
|
|
<!-- Sticky topic filter -->
|
|
<nav class="sticky top-20 hidden w-52 flex-none flex-col self-start text-gray-500 xl:flex dark:text-gray-400">
|
|
<p class="mb-3 text-xs font-semibold tracking-widest text-gray-400 uppercase dark:text-gray-500">
|
|
Browse
|
|
</p>
|
|
<button
|
|
type="button"
|
|
@click="clear()"
|
|
class="flex items-center justify-between py-1.5 text-left text-sm transition-colors"
|
|
:class="!filtering() ? 'text-black dark:text-white font-medium' : 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100'"
|
|
>
|
|
<span class="flex items-center gap-2.5">
|
|
<span>All guides</span>
|
|
</span>
|
|
<span class="text-xs tabular-nums text-gray-300 dark:text-gray-600">{{ len .RegularPagesRecursive }}</span>
|
|
</button>
|
|
{{- range $i, $tag := $tagOrder }}
|
|
{{- $tagData := index hugo.Data.tags $tag }}
|
|
{{- $pages := where $.RegularPagesRecursive "Params.tags" "intersect" (slice $tag) }}
|
|
{{- if $pages }}
|
|
<button
|
|
type="button"
|
|
@click="selectTag('{{ $tag }}')"
|
|
class="flex items-center justify-between py-1.5 text-left text-sm transition-colors"
|
|
:class="tagFilter === '{{ $tag }}' ? 'text-black dark:text-white font-medium' : 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100'"
|
|
>
|
|
<span class="flex items-center gap-2.5">
|
|
<span>{{ $tagData.title }}</span>
|
|
</span>
|
|
<span class="text-xs tabular-nums text-gray-300 dark:text-gray-600">{{ len $pages }}</span>
|
|
</button>
|
|
{{- end }}
|
|
{{- end }}
|
|
</nav>
|
|
|
|
<!-- Guide list -->
|
|
<div
|
|
class="min-w-0 flex-1"
|
|
:class="{ 'guides-flat': filtering() }"
|
|
>
|
|
<!-- Featured: curated highlights, shown only in the default view -->
|
|
{{- $featured := where $.RegularPagesRecursive "Params.featured" true }}
|
|
{{- with $featured }}
|
|
<div x-show="!filtering()" class="mb-12">
|
|
<p class="mb-4 flex items-center gap-1.5 text-xs font-semibold tracking-widest text-gray-400 uppercase dark:text-gray-500">
|
|
<span class="icon-svg icon-sm text-blue-500">{{ partialCached "icon" "star" "star" }}</span>
|
|
Featured
|
|
</p>
|
|
<div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
|
|
{{- range .ByWeight }}
|
|
<a
|
|
href="{{ .Permalink }}"
|
|
class="group flex flex-col gap-1.5 rounded-lg border border-gray-200 p-4 transition-colors hover:border-gray-300 dark:border-gray-800 dark:hover:border-gray-700"
|
|
>
|
|
<span class="font-medium leading-snug text-gray-900 transition-colors group-hover:text-blue-600 dark:text-gray-100 dark:group-hover:text-blue-400">{{ .Title }}</span>
|
|
<span class="line-clamp-2 text-sm leading-relaxed text-gray-500 dark:text-gray-400">{{ .Summary }}</span>
|
|
</a>
|
|
{{- end }}
|
|
</div>
|
|
</div>
|
|
{{- end }}
|
|
|
|
<!-- Filtering meta bar -->
|
|
<div
|
|
x-show="filtering()"
|
|
x-cloak
|
|
class="mb-8 flex items-center justify-between border-b border-gray-200 pb-4 dark:border-gray-800"
|
|
>
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
<span class="font-semibold text-gray-900 dark:text-gray-100" x-text="resultCount()"></span>
|
|
of {{ len .RegularPagesRecursive }} guides
|
|
</p>
|
|
<button
|
|
@click="clear()"
|
|
class="text-sm font-medium text-blue-600 transition hover:underline dark:text-blue-400"
|
|
>
|
|
Clear filter
|
|
</button>
|
|
</div>
|
|
|
|
{{- range $i, $tag := $tagOrder }}
|
|
{{- $tagData := index hugo.Data.tags $tag }}
|
|
{{- $pages := where $.RegularPagesRecursive "Params.tags" "intersect" (slice $tag) }}
|
|
{{- if $pages }}
|
|
<section
|
|
id="{{ $tag }}"
|
|
data-tag="{{ $tag }}"
|
|
x-show="sectionVisible($el)"
|
|
class="scroll-mt-16 border-t border-gray-200 py-12 first:border-t-0 first:pt-0 dark:border-gray-800"
|
|
>
|
|
<div class="mb-6" x-show="!filtering()">
|
|
<p class="mb-1 text-xs font-semibold tracking-widest text-gray-400 uppercase dark:text-gray-500">
|
|
{{- printf "%02d" (add $i 1) -}}
|
|
</p>
|
|
<h2 class="text-2xl font-bold">{{ $tagData.title }}</h2>
|
|
{{- with $tagData.description }}
|
|
<p class="mt-1 text-gray-500 dark:text-gray-400">{{ . }}</p>
|
|
{{- end }}
|
|
</div>
|
|
<div class="divide-y divide-gray-200 dark:divide-gray-800">
|
|
{{- range $pages }}
|
|
{{- $tagTitles := slice }}
|
|
{{- range .Params.tags }}{{ $tagTitles = $tagTitles | append (index hugo.Data.tags .).title }}{{ end }}
|
|
{{- $search := lower (printf "%s %s %s %s" .Title .Summary (delimit .Params.tags " ") (delimit $tagTitles " ")) }}
|
|
{{- $search = $search | replaceRE "\\s+" " " }}
|
|
<div
|
|
data-guide
|
|
data-search="{{ $search }}"
|
|
x-show="rowVisible($el)"
|
|
class="grid grid-cols-[5fr_6fr] items-start gap-6 py-4"
|
|
>
|
|
<a
|
|
href="{{ .Permalink }}"
|
|
class="font-medium leading-snug text-gray-900 hover:underline dark:text-gray-100"
|
|
>{{ .Title }}</a>
|
|
<span class="text-sm leading-relaxed text-gray-500 dark:text-gray-400">{{ .Summary }}</span>
|
|
</div>
|
|
{{- end }}
|
|
</div>
|
|
</section>
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
<!-- No matches -->
|
|
<div
|
|
x-show="filtering() && resultCount() === 0"
|
|
x-cloak
|
|
class="flex flex-col items-center justify-center gap-3 py-24 text-center"
|
|
>
|
|
<span class="icon-svg icon-lg text-gray-300 dark:text-gray-600">
|
|
{{ partialCached "icon" "magnifying-glass" "magnifying-glass" }}
|
|
</span>
|
|
<p class="text-gray-500 dark:text-gray-400">
|
|
No guides match “<span class="font-medium text-gray-700 dark:text-gray-200" x-text="query"></span>”.
|
|
</p>
|
|
<button
|
|
@click="clear()"
|
|
class="text-sm font-medium text-blue-600 transition hover:underline dark:text-blue-400"
|
|
>
|
|
Clear filter
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{ end }}
|