From 28b1ee8408797d0bf47ddcf0780c79778c983deb Mon Sep 17 00:00:00 2001
From: itchyny <itchyny@cybozu.co.jp>
Date: Sat, 24 Jun 2023 13:05:39 +0900
Subject: [PATCH] Redesign website

* Bump up Bootstrap to v5.3.1, Bootstrap Icon to v1.10.5.
* Use autoComplete.js to drop dependency on jQuery and typeahead.js.
* Support dark mode.
* New svg logo and icon with responsive color mode support.
* Normalize section ids to lower kebab-case for easiness of linking.
* Use relative paths for links for local development (--root /output).
* Various markup cleanups and accessibility improvements.
---
 .github/workflows/website.yml         |   2 +-
 Makefile.am                           |   6 +-
 docs/README.md                        |  14 +-
 docs/build_website.py                 |  37 ++++--
 docs/content/download/default.yml     |   6 +-
 docs/content/index.yml                |   8 +-
 docs/content/manual/manual.yml        |   5 +-
 docs/content/manual/v1.3/manual.yml   |   2 +-
 docs/content/manual/v1.4/manual.yml   |   2 +-
 docs/content/manual/v1.5/manual.yml   |   2 +-
 docs/content/manual/v1.6/manual.yml   |   2 +-
 docs/public/css/base.css              | 173 ------------------------
 docs/public/css/base.scss             | 181 --------------------------
 docs/public/css/style.css             |  99 ++++++++++++++
 docs/public/icon.png                  | Bin 0 -> 4963 bytes
 docs/public/icon.svg                  |   1 +
 docs/public/jq.png                    | Bin 8195 -> 0 bytes
 docs/public/jq.svg                    |   1 +
 docs/public/js/manual-search.js       |  80 +++++-------
 docs/site.yml                         |   9 --
 docs/templates/default.html.j2        |  41 +++---
 docs/templates/index.html.j2          |  91 ++++++-------
 docs/templates/manual.html.j2         | 179 +++++++++++++------------
 docs/templates/shared/_footer.html.j2 |  20 +--
 docs/templates/shared/_head.html.j2   |  32 +++--
 docs/templates/shared/_navbar.html.j2 |  63 +++++----
 26 files changed, 401 insertions(+), 655 deletions(-)
 delete mode 100644 docs/public/css/base.css
 delete mode 100644 docs/public/css/base.scss
 create mode 100644 docs/public/css/style.css
 create mode 100644 docs/public/icon.png
 create mode 100644 docs/public/icon.svg
 delete mode 100644 docs/public/jq.png
 create mode 100644 docs/public/jq.svg
 delete mode 100644 docs/site.yml

diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
index 9de1aa6259..6fc8b8095a 100644
--- a/.github/workflows/website.yml
+++ b/.github/workflows/website.yml
@@ -20,7 +20,7 @@ jobs:
       - name: Setup Python
         uses: actions/setup-python@v4
         with:
-          python-version: '3.10'
+          python-version: '3.11'
           cache: pipenv
       - name: Install pipenv
         run: pip install pipenv
diff --git a/Makefile.am b/Makefile.am
index e3b3e5a244..00133f6d87 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -197,14 +197,10 @@ endif
 
 ### Packaging
 
-docs/site.yml: configure.ac
-	sed 's/^jq_version: .*/jq_version: "$(VERSION)"/' $@ > $@.new
-	mv $@.new $@
-
 install-binaries: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) install-exec
 
-DOC_FILES = docs/content docs/public docs/templates docs/site.yml       \
+DOC_FILES = docs/content docs/public docs/templates                     \
         docs/Pipfile docs/Pipfile.lock docs/build_manpage.py            \
         docs/build_mantests.py docs/build_website.py docs/README.md     \
         docs/validate_manual_schema.py docs/manual_schema.yml
diff --git a/docs/README.md b/docs/README.md
index 52fdd59b82..3ea88160cc 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -11,16 +11,16 @@ need `python3` and `pipenv`. You can install `pipenv` like so:
 
 Though, you may need to say `pip3` instead, depending on your system. Once
 you have `pipenv` installed, you can install the dependencies by running
-`pipenv sync` from the `docs` directory.
+`pipenv sync` from the `docs/` directory.
 
 Also, you may need to run `virtualenv -p /usr/bin/python3 venv/` and
 then `source venv/bin/activate`, and only then `pipenv sync`.
 
 Once this is done, rerun `./configure` in the jq root directory and then
-the Makefile will be able to generate the jq manpage.  You can also just
-run `pipenv run build_manpage.py` in the `docs` directory to build the
-`jq.1` page manually, and `pipenv run build_mantests.py` to build the
-contents of `tests/man.test` and `tests/manonig.test`.
+the `Makefile` will be able to generate the jq manpage.  You can just run
+`make jq.1` to build the manpage manually, and `make tests/man.test` to
+update the manual tests.
 
-To build the website, run `pipenv run ./build_website.py` from inside
-the `docs` directory.
+To build the website, run `pipenv run python3 build_website.py --root /output`
+in the `docs/` directory. To serve them locally, you can run
+`python3 -m http.server`.
diff --git a/docs/build_website.py b/docs/build_website.py
index 5867f8c0a6..2b3afc77e2 100755
--- a/docs/build_website.py
+++ b/docs/build_website.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python3
+import argparse
 import glob
 import itertools
 from jinja2 import Environment, FileSystemLoader, select_autoescape, pass_context
@@ -10,6 +11,10 @@
 import shutil
 import yaml
 
+parser = argparse.ArgumentParser()
+parser.add_argument('--root', default='/jq')
+args = parser.parse_args()
+
 env = Environment(
     loader=FileSystemLoader('templates'),
     autoescape=select_autoescape(['html.j2']),
@@ -21,40 +26,51 @@ def load_yml_file(fn):
         return yaml.safe_load(f)
 
 
+env.globals['url'] = 'https://jqlang.github.io/jq'
+env.globals['root'] = args.root
+
 env.filters['search_id'] = lambda input: input.replace(r'`', '')
-env.filters['section_id'] = lambda input: re.sub(r"[^a-zA-Z0-9_]", '', input)
-env.filters['entry_id'] = lambda input: re.sub(r"[ `]", '', input)
+env.filters['section_id'] = lambda input: re.sub(
+    r'[^-a-zA-Z0-9_]', '', input.replace(' ', '-')).lower()
+env.filters['entry_id'] = lambda input: re.sub(
+    r'^(split|first-last-nth)$',
+    r'\1' + ('-1' if ';' not in input else '-2'),  # avoid id conflict
+    re.sub(
+        r'\b([^-]+)(?:-\1)+\b',
+        r'\1',  # e.g. range-range-range -> range
+        re.sub(r' ?/ ?|,? ', '-',
+               re.sub(r'[`;]|: .*|\(.*?\)| \[.+\]', '', input)))).lower()
 env.filters['markdownify'] = lambda input: Markup(markdown(input))
-env.filters['no_paragraph'] = lambda input: Markup(re.sub(r"</?p>", '', input))
+env.filters['no_paragraph'] = lambda input: Markup(re.sub(r'</?p>', '', input))
 
 env.globals['unique_id'] = pass_context(
     lambda ctx: str(next(ctx['unique_ctr'])))
 
-env.globals.update(load_yml_file('site.yml'))
 
-env.globals['navigation'] = ['tutorial', 'download', 'manual']
+def raise_handler(message):
+    raise Exception(message)
+
+
+env.globals['raise'] = raise_handler
 
 
-def generate_file(env, fname='content/1.tutorial/default.yml'):
+def generate_file(env, fname):
     path, base = os.path.split(fname)
     path = os.path.relpath(path, 'content')
     if path == '.':
         path = ''
-        slug = 'index'
         permalink = ''
     else:
-        slug = os.path.basename(path)
         permalink = path + '/'
 
     output_dir = os.path.join('output', path)
     output_path = os.path.join(output_dir, 'index.html')
 
-    template_name = re.sub(r".yml$", '.html.j2', base)
+    template_name = re.sub(r'.yml$', '.html.j2', base)
 
     ctx = load_yml_file(fname)
     ctx.update(unique_ctr=itertools.count(1),
                permalink=permalink,
-               slug=slug,
                navitem=path)
     os.makedirs(output_dir, exist_ok=True)
     env.get_template(template_name).stream(ctx).dump(output_path,
@@ -72,6 +88,7 @@ def copy_public_files(root=''):
             shutil.copyfile(f.path, dst)
 
 
+os.makedirs('output', exist_ok=True)
 copy_public_files()
 
 for fn in glob.glob('content/**/*.yml', recursive=True):
diff --git a/docs/content/download/default.yml b/docs/content/download/default.yml
index 4f39c6128c..970f1c85a7 100644
--- a/docs/content/download/default.yml
+++ b/docs/content/download/default.yml
@@ -196,9 +196,9 @@ body:
       #### Building the documentation
 
       jq's documentation is compiled into static HTML using Python.
-      To build the docs, run `pipenv run python3 build_website.py` from
-      the docs/ subdirectory. To serve them locally, you can run
-      `python3 -m SimpleHTTPServer`. You'll need a few Python dependencies,
+      To build the docs, run `pipenv run python3 build_website.py --root /output`
+      in the `docs/` directory. To serve them locally, you can run
+      `python3 -m http.server`. You'll need a few Python dependencies,
       which can be installed by following the instructions in `docs/README.md`.
 
       The man page is built by `make jq.1`, or just `make`, also from
diff --git a/docs/content/index.yml b/docs/content/index.yml
index 02ecafe5e7..82dcde9845 100644
--- a/docs/content/index.yml
+++ b/docs/content/index.yml
@@ -24,7 +24,7 @@ body3: |
 
 tail: |
 
-  Go read the [tutorial](/jq/tutorial/) for more, or the [manual](/jq/manual/)
+  Go read the [tutorial](./tutorial/) for more, or the [manual](./manual/)
   for *way* more.
 
   Have a question related to jq? You can seek answers on [Stack Overflow](https://stackoverflow.com/)
@@ -34,7 +34,7 @@ tail: |
 news:
   - date: 1 November 2018
     body: |
-      jq 1.6 released. See installation options on the [download](/jq/download/)
+      jq 1.6 released. See installation options on the [download](./download/)
       page, and the [release notes](https://github.com/jqlang/jq/releases/tag/jq-1.6)
       for details.
 
@@ -44,7 +44,7 @@ news:
       jq 1.5 released, including new datetime, math, and regexp functions,
       try/catch syntax, array and object destructuring, a streaming parser,
       and a module system. See installation options on the
-      [download](/jq/download/) page, and the
+      [download](./download/) page, and the
       [release notes](https://github.com/jqlang/jq/releases/tag/jq-1.5)
       for details.
 
@@ -63,7 +63,7 @@ news:
   - date: 09 June 2014
     body: |
 
-      jq 1.4 (finally) released! Get it on the [download](/jq/download/) page.
+      jq 1.4 (finally) released! Get it on the [download](./download/) page.
 
   - date: 19 May 2013
     body: |
diff --git a/docs/content/manual/manual.yml b/docs/content/manual/manual.yml
index 89252ac243..e4c8c64bca 100644
--- a/docs/content/manual/manual.yml
+++ b/docs/content/manual/manual.yml
@@ -3,9 +3,8 @@ headline: jq Manual (development version)
 
 history: |
 
-  *For released versions, see [jq 1.6](/jq/manual/v1.6),
-  [jq 1.5](/jq/manual/v1.5),  [jq 1.4](/jq/manual/v1.4)
-  or [jq 1.3](/jq/manual/v1.3).*
+  *For released versions, see [jq 1.6](./v1.6/), [jq 1.5](./v1.5/),
+  [jq 1.4](./v1.4/) or [jq 1.3](./v1.3/).*
 
 body: |
 
diff --git a/docs/content/manual/v1.3/manual.yml b/docs/content/manual/v1.3/manual.yml
index c018e3142c..8cab6204bf 100644
--- a/docs/content/manual/v1.3/manual.yml
+++ b/docs/content/manual/v1.3/manual.yml
@@ -4,7 +4,7 @@ headline: jq 1.3 Manual
 history: |
 
   *The manual for the development version of jq can be found
-  [here](/jq/manual).*
+  [here](../).*
 
 body: |
 
diff --git a/docs/content/manual/v1.4/manual.yml b/docs/content/manual/v1.4/manual.yml
index 6cc1c5913b..cbfb82631e 100644
--- a/docs/content/manual/v1.4/manual.yml
+++ b/docs/content/manual/v1.4/manual.yml
@@ -4,7 +4,7 @@ headline: jq 1.4 Manual
 history: |
 
   *The manual for the development version of jq can be found
-  [here](/jq/manual).*
+  [here](../).*
 
 body: |
 
diff --git a/docs/content/manual/v1.5/manual.yml b/docs/content/manual/v1.5/manual.yml
index be2f1c562a..14f70532f4 100644
--- a/docs/content/manual/v1.5/manual.yml
+++ b/docs/content/manual/v1.5/manual.yml
@@ -4,7 +4,7 @@ headline: jq 1.5 Manual
 history: |
 
   *The manual for the development version of jq can be found
-  [here](/jq/manual).*
+  [here](../).*
 
 body: |
 
diff --git a/docs/content/manual/v1.6/manual.yml b/docs/content/manual/v1.6/manual.yml
index be62cab6cd..0a3d6002a2 100644
--- a/docs/content/manual/v1.6/manual.yml
+++ b/docs/content/manual/v1.6/manual.yml
@@ -4,7 +4,7 @@ headline: jq 1.6 Manual
 history: |
 
   *The manual for the development version of jq can be found
-  [here](/jq/manual).*
+  [here](../).*
 
 body: |
 
diff --git a/docs/public/css/base.css b/docs/public/css/base.css
deleted file mode 100644
index e9bb818dc6..0000000000
--- a/docs/public/css/base.css
+++ /dev/null
@@ -1,173 +0,0 @@
-body {
-    padding-top: 80px;
-}
-
-.container {
-    max-width: 970px;
-}
-
-/* index.liquid *******************************************/
-#blurb {
-    padding-top: 40px;
-}
-#blurb p {
-    font-size: 1.9em;
-}
-#blurb .btn-group {
-    margin: 4px;
-}
-
-#multiblurb {
-    line-height: 1.7;
-    text-align: center;
-    font-size: 12pt;
-}
-#multiblurb code {
-    border: 0;
-    font-size: 12pt;
-}
-
-#news {
-    font-size: 12pt;
-}
-#news .date {
-    font-style: italic;
-}
-
-/* default.liquid *****************************************/
-.tutorial-example {
-    position: relative;
-    margin-bottom: 10px;
-}
-.tutorial-example pre {
-    margin-bottom: 0px;
-}
-.tutorial-example a {
-    position: absolute;
-    top: 0px;
-    right: 0px;
-    padding: 15px 8px;
-    color: #777777;
-    font-weight: bold;
-    line-height: 10px;
-    font-size: 12px;
-    border-left: 1px solid #DDDDDD;
-    display: block;
-}
-.tutorial-example .accordion-body pre {
-    margin: 0 4px;
-    border-top: 0;
-    border-top-left-radius: 0;
-    border-top-right-radius: 0;
-}
-
-@media print {
-    .tutorial-example a {
-        display: none;
-    }
-}
-/* manual.liquid ******************************************/
-section {
-    padding-top: 24px;
-}
-
-h3 code {
-    border: 0;
-    font-size: 20px;
-}
-
-@media (max-width: 991px) {
-    #navcolumn {
-        /* Put nav column above manual content */
-        position: relative !important;
-        margin-bottom: 60px;
-    }
-}
-@media (min-width: 992px) {
-    #manualcontent {
-        /* Put nav column left of manual content */
-        padding-left: 280px;
-    }
-}
-.nav-pills {
-    margin-bottom: 20px;
-}
-    .nav-pills li a {
-        padding: 8px 12px;
-}
-
-.manual-example table {
-    border-top: 1px solid #E5E5E5;
-}
-.manual-example table td {
-    white-space: pre-wrap;
-    font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
-}
-.manual-example table td.jqprogram {
-    font-weight: bold;
-}
-.manual-example table th {
-    text-align: right;
-    padding-right: 10px;
-}
-
-@media print {
-    #navcolumn {
-        display: none !important;
-    }
-
-    .manual-example {
-        display: block !important;
-        height: auto !important;
-    }
-
-    .jqplay-btn {
-        display: none !important;
-    }
-}
-/* shared/_footer.liquid **********************************/
-footer {
-    background-color: #F5F5F5;
-    padding: 20px 0;
-    margin-top: 40px;
-    color: #999999;
-    text-align: center;
-}
-footer p {
-    margin: 8px 0;
-}
-
-/* typeahead **********************************************/
-.twitter-typeahead {
-    width: 100%;
-}
-
-.tt-menu {
-    width: 100%;
-    background-color: #fff;
-    padding: 8px 0;
-    border: 1px solid #ccc;
-    border: 1px solid rgba(0, 0, 0, 0.2);
-    -webkit-border-radius: 8px;
-    -moz-border-radius: 8px;
-    border-radius: 8px;
-    -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-    -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-}
-
-.tt-suggestion {
-    padding: 3px 20px;
-}
-.tt-suggestion:hover {
-    cursor: pointer;
-    color: #fff;
-    background-color: #446e9b;
-}
-.tt-suggestion.tt-cursor {
-    color: #fff;
-    background-color: #446e9b;
-}
-.tt-suggestion p {
-    margin: 0;
-}
diff --git a/docs/public/css/base.scss b/docs/public/css/base.scss
deleted file mode 100644
index ffef3de440..0000000000
--- a/docs/public/css/base.scss
+++ /dev/null
@@ -1,181 +0,0 @@
-@charset "utf-8";
-body {
-    padding-top: 80px;
-}
-
-.container {
-    max-width: 970px;
-}
-
-/* index.liquid *******************************************/
-
-#blurb {
-    padding-top: 40px;
-    p {
-        font-size: 1.9em;
-    }
-    .btn-group {
-        margin: 4px;
-    }
-}
-
-#multiblurb {
-    line-height: 1.7;
-    text-align: center;
-    font-size: 12pt;
-    code {
-        border: 0;
-        font-size: 12pt;
-    }
-}
-
-#news {
-    font-size: 12pt;
-    .date {
-          font-style: italic;
-    }
-}
-
-/* default.liquid *****************************************/
-
-.tutorial-example {
-    position: relative;
-    margin-bottom: 10px;
-    pre {
-        margin-bottom: 0px;
-    }
-    a {
-        position: absolute;
-        top: 0px;
-        right: 0px;
-        padding: 15px 8px;
-        color: #777777;
-        font-weight: bold;
-        line-height: 10px;
-        font-size: 12px;
-        border-left: 1px solid #DDDDDD;
-        display: block;
-    }
-    .accordion-body pre {
-        margin: 0 4px;
-        border-top: 0;
-        border-top-left-radius: 0;
-        border-top-right-radius: 0;
-    }
-}
-
-@media print {
-    .tutorial-example a {
-        display: none;
-    }
-}
-
-/* manual.liquid ******************************************/
-
-section {
-    padding-top: 24px;
-}
-
-h3 code {
-    border: 0;
-    font-size: 20px;
-}
-
-@media(max-width: 991px){
-    #navcolumn {
-        /* Put nav column above manual content */
-        position: relative !important;
-        margin-bottom: 60px;
-    }
-}
-
-@media(min-width: 992px) {
-    #manualcontent {
-        /* Put nav column left of manual content */
-        padding-left: 280px;
-    }
-}
-
-.nav-pills {
-    li a {
-        padding: 8px 12px;
-    }
-    margin-bottom: 20px;
-}
-
-.manual-example table {
-    border-top: 1px solid #E5E5E5;
-    td {
-        white-space: pre-wrap;
-        font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
-    }
-    td.jqprogram {
-        font-weight: bold;
-    }
-    th {
-        text-align: right;
-        padding-right: 10px;
-    }
-}
-
-@media print {
-    #navcolumn {
-        display: none !important;
-    }
-    .manual-example {
-        display: block !important;
-        height: auto !important;
-    }
-    .jqplay-btn {
-        display: none !important;
-    }
-}
-
-/* shared/_footer.liquid **********************************/
-
-footer {
-    background-color: #F5F5F5;
-    padding: 20px 0;
-    margin-top: 40px;
-    color: #999999;
-    text-align: center;
-    p {
-        margin: 8px 0;
-    }
-}
-
-/* typeahead **********************************************/
-
-.twitter-typeahead {
-    width: 100%;
-}
-
-.tt-menu {
-    width: 100%;
-    background-color: #fff;
-    padding: 8px 0;
-    border: 1px solid #ccc;
-    border: 1px solid rgba(0, 0, 0, 0.2);
-    -webkit-border-radius: 8px;
-       -moz-border-radius: 8px;
-            border-radius: 8px;
-    -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-       -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-            box-shadow: 0 5px 10px rgba(0,0,0,.2);
-}
-
-.tt-suggestion {
-    padding: 3px 20px;
-    &:hover {
-        cursor: pointer;
-        color: #fff;
-        background-color: #446e9b;
-    }
-    &.tt-cursor {
-        color: #fff;
-        background-color: #446e9b;
-    }
-    p {
-        margin: 0;
-    }
-}
diff --git a/docs/public/css/style.css b/docs/public/css/style.css
new file mode 100644
index 0000000000..d63e828275
--- /dev/null
+++ b/docs/public/css/style.css
@@ -0,0 +1,99 @@
+main {
+  padding: 1rem;
+
+  & * {
+    scroll-margin-top: 4rem;
+  }
+
+  @media print {
+    width: 100%!important;
+    --bs-code-color: --bs-body-color;
+  }
+}
+
+header {
+  z-index: 1050!important; /* higher than #contents */
+}
+
+section[id] {
+  display: flow-root;
+
+  > :first-child {
+    .icon-link {
+      opacity: 0;
+
+      &:focus {
+        opacity: .8;
+      }
+    }
+
+    &:hover .icon-link {
+      opacity: 1;
+    }
+  }
+}
+
+.offcanvas[aria-modal=true] .nav-link {
+  padding: .7rem;
+}
+.offcanvas-md {
+  --bs-offcanvas-width: auto;
+}
+
+ul {
+  list-style: none;
+  padding-left: 1rem;
+}
+
+pre {
+  margin: 0 .5rem 1rem;
+  padding: .5rem 1rem;
+  background-color: var(--bs-secondary-bg-subtle);
+  border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color);
+}
+
+button{
+  &[aria-expanded=false] .bi-chevron-down {
+    display: none;
+  }
+
+  &[aria-expanded=true] .bi-chevron-right {
+    display: none;
+  }
+}
+
+mark {
+  padding: 0;
+}
+
+.container-searchbox {
+  position: relative;
+
+  & input:focus ~ kbd {
+    display: none;
+  }
+
+  & ul {
+    position: absolute;
+    width: 100%;
+    top: 100%;
+    padding: 0;
+    background-color: var(--bs-body-bg);
+    border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color);
+  }
+
+  & li {
+    padding: .3em .6em;
+    white-space: nowrap;
+    overflow-x: hidden;
+
+    &[aria-selected=true] {
+      background-color: var(--bs-secondary-bg);
+    }
+
+    &:hover {
+      cursor: pointer;
+      background-color: var(--bs-secondary-bg-subtle);
+    }
+  }
+}
diff --git a/docs/public/icon.png b/docs/public/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..cfdc8ecda9840955ed9b0031693acbe5606842d6
GIT binary patch
literal 4963
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
YkV0_R5Mv=?@PZ7YrmU@01-A+NKgP>a@c;k-

literal 0
HcmV?d00001

diff --git a/docs/public/icon.svg b/docs/public/icon.svg
new file mode 100644
index 0000000000..1f518f047f
--- /dev/null
+++ b/docs/public/icon.svg
@@ -0,0 +1 @@
+<svg width="48" height="48" version="1.0" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"><style>@media(prefers-color-scheme:dark){g[fill]{filter:invert(1)}}</style><g transform="matrix(.011 0 0 .011 1.94 12)"><g fill="#444"><circle cx="220" cy="1570" r="190"/><path d="m1330 312c-30 41-698 1492-710 1528s25 66 100 101c77 36 102 34 124 3s701-1477 716-1525c15-47-20-73-84-104s-116-44-146-3z"/></g><g fill="#111"><circle cx="2410" cy="200" r="190"/><path d="m1830 655c0 129 0 130 70 130h360v423c0 246-5 474-10 506-19 112-88 176-190 176-59 0-120-27-147-66-51-73-65-84-99-84-31 0-40 8-104 93-84 111-86 129-32 189 227 252 692 187 836-118 53-112 56-153 56-795v-514c0-64-30-100-100-100h-490c-149 0-150 0-150 160zM2867 1233c30 302 250 499 438 527 144 19 244 0 375-90v410c0 81 0 100 150 100 136 0 150-20 150-150v-1420c0-99-19-100-150-100s-139 15-139 82c-103-89-351-149-523-49s-342 278-301 690zm803-107c0 292-105 356-237 356-178 0-249-147-273-296-27-168 25-420 262-420 171 0 248 89 248 360z"/></g></g></svg> 
diff --git a/docs/public/jq.png b/docs/public/jq.png
deleted file mode 100644
index 41d6d391dbf86ed4d36559d19f2cd3885b077096..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 8195
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

diff --git a/docs/public/jq.svg b/docs/public/jq.svg
new file mode 100644
index 0000000000..1f9e2ddf6d
--- /dev/null
+++ b/docs/public/jq.svg
@@ -0,0 +1 @@
+<svg width="400" height="220" version="1.0" viewBox="0 0 400 220" xmlns="http://www.w3.org/2000/svg"><style>@media (prefers-color-scheme:dark){g[fill]{filter:invert(1);stroke:none}}</style><g transform="scale(.1)" stroke="#fff" stroke-width="10"><g fill="#444"><circle cx="220" cy="1570" r="190"/><path d="m1330 312c-30 41-698 1492-710 1528s25 66 100 101c77 36 102 34 124 3s701-1477 716-1525c15-47-20-73-84-104s-116-44-146-3z"/></g><g fill="#111"><circle cx="2410" cy="200" r="190"/><path d="m1830 655c0 129 0 130 70 130h360v423c0 246-5 474-10 506-19 112-88 176-190 176-59 0-120-27-147-66-51-73-65-84-99-84-31 0-40 8-104 93-84 111-86 129-32 189 227 252 692 187 836-118 53-112 56-153 56-795v-514c0-64-30-100-100-100h-490c-149 0-150 0-150 160zM2867 1233c30 302 250 499 438 527 144 19 244 0 375-90v410c0 81 0 100 150 100 136 0 150-20 150-150v-1420c0-99-19-100-150-100s-139 15-139 82c-103-89-351-149-523-49s-342 278-301 690zm803-107c0 292-105 356-237 356-178 0-249-147-273-296-27-168 25-420 262-420 171 0 248 89 248 360z"/></g></g></svg> 
diff --git a/docs/public/js/manual-search.js b/docs/public/js/manual-search.js
index e5d6ee53ab..6c7c72d56e 100644
--- a/docs/public/js/manual-search.js
+++ b/docs/public/js/manual-search.js
@@ -1,52 +1,34 @@
-var section_names = function(q) {
-  if (!q) {
-    return [];
-  }
-  var matches = [];
-  q = q.toLowerCase();
-  $.each(section_map, function(k, v) {
-    if (k.toLowerCase().indexOf(q) != -1) {
-      matches.push(k);
-    }
-  });
-  matches.sort(function(a, b) {
-    // shortest to longest
-    return a.length - b.length;
+(() => {
+  const searchInput = document.querySelector('input#searchbox');
+  const sectionIDs = JSON.parse(document.querySelector('#section-ids').innerText);
+  const sanitize = (string) => string.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
+  new autoComplete({
+    selector: `#${searchInput.id}`,
+    wrapper: false,
+    data: {
+      src: Object.keys(sectionIDs),
+      filter: (list) => list.sort((x, y) =>
+        x.match.indexOf('<') - y.match.indexOf('<') || x.value.length - y.value.length),
+    },
+    searchEngine: (query, value) => {
+      const index = value.toLowerCase().indexOf(query.toLowerCase());
+      if (index >= 0) {
+        return sanitize(value.substring(0, index)) +
+          `<mark>${sanitize(value.substring(index, index + query.length))}</mark>` +
+          sanitize(value.substring(index + query.length));
+      }
+    },
   });
-  return matches;
-}
-var section_names_cb = function(q, cb) {
-  cb(section_names(q));
-}
-var go_to_section = function() {
-  query = $('#searchbox').val();
-  results = section_names(query);
-  if (results.length == 0) {
-    return;
-  }
-  result = results[0];
-  location.hash = '#' + section_map[result];
-  if (result != query) {
-    $('#searchbox').val(result);
-  }
-}
-$(function(){
-  $('#searchbox').typeahead(
-    {hint: false, highlight: true, minLength: 1},
-    {name: "contents", source: section_names_cb, limit: 6}
-  ).on('typeahead:selected', function(e, data) {
-    go_to_section();
+  searchInput.addEventListener('selection', (event) => {
+    event.target.value = event.detail.selection.value;
+    location.hash = `#${sectionIDs[event.detail.selection.value]}`;
   });
-  $('#searchbox').change(go_to_section);
-});
-// add "Run" button to execute examples on jqplay.org
-$(function() {
-  $.each($('.manual-example table'), function(index, value) {
-    $value = $(value)
-    var j = $value.find('tr:nth-child(2) td:first').text();
-    var q = $value.find('.jqprogram').text().replace(/^jq /, '').replace(/(\r\n|\n|\r)/gm," ").replace(/^'(.+)'$/, '$1');
-    var url = 'https://jqplay.org/jq?q=' + encodeURIComponent(q) +'&j=' + encodeURIComponent(j)
-    var $last_tr = $value.find('tr:last');
-    $last_tr.after('<tr class="jqplay-btn"><th><a href="' + url + '" class="btn btn-primary btn-sm">Run</a></th><th></th></tr><tr><th></th><th></th></tr>');
+  document.addEventListener('keydown', (event) => {
+    if (event.code === 'Slash' && !event.altKey && !event.ctrlKey && !event.metaKey
+      && !event.shiftKey && !/^(INPUT|TEXTAREA)$/.test(event.target.nodeName)) {
+      searchInput.focus();
+      searchInput.select();
+      event.preventDefault();
+    }
   });
-});
+})();
diff --git a/docs/site.yml b/docs/site.yml
deleted file mode 100644
index e4990aeb9e..0000000000
--- a/docs/site.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-# The key value pairs found below are available within the templates.
-
-url: https://jqlang.github.io/jq
-
-# This line is modified by the Makefile. To change the version number,
-# edit the Autoconf version number at the top of configure.ac
-jq_version: "1.6-159-gcff5336-dirty"
-
-root: '/jq'
diff --git a/docs/templates/default.html.j2 b/docs/templates/default.html.j2
index 7830eb3726..67030e0566 100644
--- a/docs/templates/default.html.j2
+++ b/docs/templates/default.html.j2
@@ -2,32 +2,29 @@
 <html lang="en">
 {% include "shared/_head.html.j2" %}
 
-  <body id="{{slug}}">
+  <body>
 {% include "shared/_navbar.html.j2" %}
 
-    <div class="container">
-      <div class="row">
-        <h1>{{headline}}</h1>
-        {% for item in body %}
-          {% if item.text %}
-            {{ item.text | replace('$JQ_VERSION', jq_version) | markdownify }}
-          {% endif %}
+    <main id="main" class="container-lg">
+      <h1>{{ headline }}</h1>
+      {%- for item in body %}
+      {%- if item.text %}
+      {{ item.text | markdownify }}
+      {%- endif %}
 
-          {% if item.command %}
-            {% set resultID = unique_id() %}
-            <div class="tutorial-example">
-              <div class="accordion-heading">
-                <pre>{{item.command}}</pre>
-                <a data-toggle="collapse" href="#result{{resultID}}">Show result</a>
-              </div>
-              <div id="result{{resultID}}" class="accordion-body collapse">
-                <pre>{{item.result}}</pre>
-              </div>
-            </div>
-          {% endif %}
-        {% endfor %}
+      {%- if item.command %}
+      {%- set resultID = unique_id() %}
+      <div class="tutorial-example mb-3">
+        <div class="d-flex accordion-heading me-2">
+          <pre class="flex-grow-1 me-0 mb-0" tabindex="0">{{ item.command }}</pre>
+          <button type="button" class="btn btn-sm btn-secondary text-body-secondary bg-secondary-subtle link-body-emphasis flex-shrink-0 d-flex align-items-center border border-start-0 d-print-none"
+            data-bs-toggle="collapse" data-bs-target="#result{{ resultID }}" aria-expanded="false" aria-controls="result{{ resultID }}">Show result</button>
+        </div>
+        <pre id="result{{ resultID }}" class="accordion-body collapse p-3 border-top-0 d-print-block" tabindex="0">{{ item.result }}</pre>
       </div>
-    </div>
+      {%- endif %}
+      {%- endfor %}
+    </main>
 
 {% include "shared/_footer.html.j2" %}
   </body>
diff --git a/docs/templates/index.html.j2 b/docs/templates/index.html.j2
index 4ec30007fb..3fd1566ec4 100644
--- a/docs/templates/index.html.j2
+++ b/docs/templates/index.html.j2
@@ -2,71 +2,54 @@
 <html lang="en">
 {% include "shared/_head.html.j2" %}
 
-  <body id="{{ slug }}">
+  <body>
 {% include "shared/_navbar.html.j2" %}
 
-    <div class="container">
+    <main id="main" class="container-lg">
       <div class="row">
-        <div class="jumbotron">
-          <div class="row">
-            <div class="col-sm-6">
-              <img src="{{root}}/jq.png" class="img-responsive" alt="jq logo"
-                  width="400" height="220">
-            </div>
-            <div class="col-sm-6">
-              <div id="blurb">
-                {{blurb | markdownify}}
-
-                <div class="btn-toolbar" role="toolbar">
-                  <div class="btn-group" role="group">
-                    <a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
-                      Download jq 1.6
-                      <span class="caret"></span>
-                    </a>
-                    <ul class="dropdown-menu">
-                      <li><a href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64">Linux (64-bit)</a></li>
-                      <li><a href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-osx-amd64">macOS (64-bit)</a></li>
-                      <li><a href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-win64.exe">Windows (64-bit)</a></li>
-                      <li><a href="{{root}}/download/">Other platforms, older versions, and source</a></li>
-                    </ul>
-                  </div>
-
-                  <div class="btn-group">
-                    <a class="btn btn-primary" href="https://jqplay.org">
-                      Try online at jqplay.org!
-                    </a>
-                  </div>
-                </div>
-              </div>
-            </div>
+        <div class="col-md-6 text-center p-3">
+          <h1><img src="{{ root }}/jq.svg" class="img-fluid" alt="jq" width="400" height="220"></h1>
+        </div>
+        <div class="col-md-6 d-flex flex-column justify-content-center text-center align-items-center">
+          <h2 class="px-1" style="width:16em">{{ blurb }}</h2>
+          <div class="btn-group d-print-none" role="group">
+            <button type="button" class="btn btn-primary dropdown-toggle text-nowrap" data-bs-toggle="dropdown" aria-expanded="false">
+              Download jq 1.6
+              <span class="caret"></span>
+            </button>
+            <ul class="dropdown-menu">
+              <li><a class="dropdown-item" href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64" aria-label="Link to download executable: Linux (64-bit)"><span class="bi bi-download me-2" aria-hidden="true"></span>Linux (64-bit)</a></li>
+              <li><a class="dropdown-item" href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-osx-amd64" aria-label="Link to download executable: macOS (64-bit)"><span class="bi bi-download me-2" aria-hidden="true"></span>macOS (64-bit)</a></li>
+              <li><a class="dropdown-item" href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-win64.exe" aria-label="Link to download executable: Windows (64-bit)"><span class="bi bi-download me-2" aria-hidden="true"></span>Windows (64-bit)</a></li>
+              <li><a class="dropdown-item" href="{{ root }}/download/">Other platforms, older versions, and source</a></li>
+            </ul>
+            <a class="btn btn-primary text-nowrap" href="https://jqplay.org" target="_blank" rel="noopener">
+              Try online at jqplay.org!<span class="bi bi-box-arrow-up-right ms-1" aria-hidden="true"></span>
+            </a>
           </div>
         </div>
       </div>
 
-      <div class="row" id="multiblurb">
-        <div class="col-sm-4">{{body1 | markdownify}}</div>
-        <div class="col-sm-4">{{body2 | markdownify}}</div>
-        <div class="col-sm-4">{{body3 | markdownify}}</div>
+      <div class="row my-3">
+        <div class="col-md-4">{{ body1 | markdownify }}</div>
+        <div class="col-md-4">{{ body2 | markdownify }}</div>
+        <div class="col-md-4">{{ body3 | markdownify }}</div>
       </div>
 
-      <div class="row" style="text-align:center; margin-top: 30px">
-        {{tail | markdownify}}
+      <div class="text-center my-3">
+        {{ tail | markdownify }}
       </div>
 
-      <div class="row">
-        <h2>News</h2>
-        <div id="news">
-          <ul>
-            {% for item in news %}
-              <li>
-                <span class="date">{{item.date}}</span>
-                {{item.body | markdownify}}
-              </li>
-            {% endfor %}
-          </ul>
-        </div>
-      </div>
-    </div>
+      <h2>News</h2>
+      <ul>
+        {%- for item in news %}
+        <li>
+          <span class="fst-italic">{{ item.date }}</span>
+          {{ item.body | markdownify }}
+        </li>
+        {%- endfor %}
+      </ul>
+    </main>
 
 {% include "shared/_footer.html.j2" %}
   </body>
diff --git a/docs/templates/manual.html.j2 b/docs/templates/manual.html.j2
index 21f9ad28e2..9a2540c8b0 100644
--- a/docs/templates/manual.html.j2
+++ b/docs/templates/manual.html.j2
@@ -2,98 +2,109 @@
 <html lang="en">
 {% include "shared/_head.html.j2" %}
 
-  <body id="{{slug}}" data-spy="scroll" data-target="#navcolumn" data-offset="100">
+  <body>
 {% include "shared/_navbar.html.j2" %}
 
-    <div class="container">
-      <div class="row">
-        <div class="affix" id="navcolumn">
+    <div class="container-lg row align-items-start mx-auto p-3">
+
+      <button type="button" class="d-md-none w-auto position-fixed bottom-0 end-0 p-2 m-3 bg-body-secondary border-0 text-body d-print-none"
+        data-bs-toggle="offcanvas" data-bs-target="#contents" aria-controls="contents" aria-expanded="false" aria-label="Toggle table of contents">
+        <span class="bi bi-list" aria-hidden="true"></span>
+      </button>
+      <nav id="contents" class="col-md-3 sticky-md-top p-3 overflow-y-auto offcanvas-md offcanvas-end d-print-none" style="top:4.5rem; height:calc(100dvh - 5.5rem);" aria-label="Table of contents">
+        <div class="d-flex justify-content-between">
           <h4>Contents</h4>
-          <ul class="nav nav-pills nav-stacked">
-            {% for section in sections %}
-              <li>
-                <a href="#{{section.title | section_id}}">{{section.title}}</a>
-              </li>
-            {% endfor %}
-          </ul>
-          <form class="form-group" onsubmit="go_to_section(); return false;">
-            <input type="text"
-                  class="form-control"
-                  placeholder="Search"
-                  autocomplete="off"
-                  id="searchbox">
-          </form>
+          <button type="button" class="btn-close d-md-none" data-bs-dismiss="offcanvas" data-bs-target="#contents" aria-label="Close table of contents"></button>
         </div>
+        <ul class="offcanvas-md-body nav nav-pills flex-column">
+          {%- for section in sections %}
+          <li class="nav-item" data-bs-dismiss="offcanvas" data-bs-target="#contents">
+            <a class="nav-link" href="#{{ section.title | section_id }}">{{ section.title }}</a>
+          </li>
+          {%- endfor %}
+        </ul>
+      </nav>
+      {%- set section_ids = {} %}
+      {%- macro check_section_id(id) -%}
+        {%- if section_ids.__contains__(id) %}
+          {{- raise('Duplicate section id: ' ~ id) }}
+        {%- endif %}
+        {%- set _ = section_ids.__setitem__(id, true) %}
+      {%- endmacro %}
+      <main id="main" class="col-md-9" data-bs-spy="scroll" data-bs-target="#contents" data-bs-threshold="0,1" data-bs-root-margin="-30% 0% -70%">
+        <h1>{{ headline }}</h1>
+        {{ history | markdownify }}
+        {{ body | markdownify }}
+        {%- for section in sections %}
+        <section id="{{ section.title | section_id }}">{{ check_section_id(section.title | section_id) }}
+          <h2>{{ section.title }} <a href="#{{ section.title | section_id }}" class="icon-link" aria-label="Link to this section: {{ section.title }}"><span class="bi bi-link-45deg" aria-hidden="true"></span></a></h2>
+          {{ section.body | markdownify if section.body }}
+          {%- for entry in section.entries %}
+          <section id="{{ entry.title | entry_id }}">{{ check_section_id(entry.title | entry_id) }}
+            <h3>
+              {{ entry.title | markdownify | no_paragraph }}
+              <a href="#{{ entry.title | entry_id }}" class="icon-link" aria-label="Link to this section: {{ entry.title }}"><span class="bi bi-link-45deg" aria-hidden="true"></span></a>
+            </h3>
+            {{ entry.body | markdownify }}
+            {%- if entry.examples %}
+            <div class="pb-3">
+              {%- set exampleID = unique_id() %}
+              <button type="button" class="btn btn-sm btn-secondary text-body-secondary bg-transparent link-body-emphasis border-0" data-bs-toggle="collapse" data-bs-target="#example{{ exampleID }}" aria-expanded="false" aria-controls="example{{ exampleID }}">
+                <span class="me-1 d-print-none" aria-hidden="true"><span class="bi bi-chevron-right"></span><span class="bi bi-chevron-down"></span></span>Example{% if entry.examples | length > 1 %}s{% endif %}
+              </button>
+              <div id="example{{ exampleID }}" class="collapse mx-3 small d-print-block">
+                {%- for example in entry.examples %}
+                <table class="table table-borderless table-sm w-auto">
+                  <tr>
+                    <th class="pe-3">Command</th>
+                    <td class="font-monospace">jq '{{ example.program }}'</td>
+                  </tr>
+                  <tr>
+                    <th>Input</th>
+                    <td class="font-monospace">{{ example.input }}</td>
+                  </tr>
+                  {%- if not example.output[0] %}
+                  <tr>
+                    <th>Output</th>
+                    <td class="fst-italic">none</td>
+                  </tr>
+                  {%- endif %}
+                  {%- for output in example.output %}
+                  <tr>
+                    <th>{% if loop.first %}Output{% endif %}</th>
+                    <td class="font-monospace">{{ output }}</td>
+                  </tr>
+                  {%- endfor %}
+                  <tr class="d-print-none">
+                    <th><a href="https://jqplay.org/jq?q={{ example.program | urlencode }}&j={{ example.input | urlencode }}" class="btn btn-outline-primary btn-sm" target="_blank" rel="noopener">Run<span class="bi bi-box-arrow-up-right ms-2" aria-hidden="true"></span></a></th>
+                    <td></td>
+                  </tr>
+                </table>
+                {%- endfor %}
+              </div>
+            </div>
+            {%- endif %}
+          </section>
+          {%- endfor %}
+        </section>
+        {%- endfor %}
+      </main>
 
-        <div id="manualcontent">
-          <h1>{{headline}}</h1>
-          {{ history | markdownify }}
-          {{ body | markdownify }}
-          {% for section in sections %}
-            <section id="{{section.title | section_id}}">
-              <h2>{{section.title}}</h2>
-              {{ (section.body | markdownify) if section.body }}
-              {% for entry in section.entries %}
-                <section id="{{entry.title | entry_id}}">
-                  <h3>
-                    {{entry.title | markdownify | no_paragraph }}
-                    {% if entry.subtitle %}<small>{{entry.subtitle}}</small>{% endif %}
-                  </h3>
-                  {{entry.body | markdownify}}
-
-                  {% if entry.examples %}
-                    <div>
-                      {% set exampleID = unique_id() %}
-                      <a data-toggle="collapse" href="#example{{exampleID}}">
-                        <i class="glyphicon glyphicon-chevron-right"></i>
-                        {% if entry.examples | length > 1 %}Examples{%else%}Example{%endif%}
-                      </a>
-                      <div id="example{{exampleID}}" class="manual-example collapse">
-                        {% for example in entry.examples %}
-                          <table>
-                            <tr><th></th><td class="jqprogram">jq '{{ example.program }}'</td></tr>
-                            <tr><th>Input</th><td>{{ example.input }}</td></tr>
-                            {% if not example.output[0] %}
-                              <tr>
-                                <th>Output</th>
-                                <td><i>none</i></td>
-                              </tr>
-                            {% endif %}
-                            {% for output in example.output %}
-                              <tr>
-                                {% if loop.first %}
-                                  <th>Output</th>
-                                {% else %}
-                                  <th></th>
-                                {% endif %}
-                                <td>{{ output }}</td>
-                              </tr>
-                            {% endfor %}
-                          </table>
-                        {% endfor %}
-                      </div>
-                    </div>
-                  {% endif %}
-                </section>
-              {% endfor %}
-            </section>
-          {% endfor %}
-        </div>
-      </div>
     </div>
 
 {% include "shared/_footer.html.j2" %}
-    <script>
-      var section_map = {
-        {% for section in sections %}
-          {% for entry in section.entries %}
-            {{entry.title | search_id | tojson }} : {{entry.title | entry_id | tojson }},
-          {% endfor %}
-          {{section.title | search_id | tojson }} : {{section.title | section_id | tojson }}
-          {% if not loop.last %},{% endif %}
-        {% endfor %}
-      };
+    <script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js"
+      integrity="sha384-xbzjoN6H5XHmAqoSSR42hZVRninPGx85y+XZQGlWxVu2y91lTmr/oD80i5cjeUBv" crossorigin="anonymous"></script>
+    <script id="section-ids" type="application/json">
+      {
+        {%- for section in sections %}
+        {%- for entry in section.entries %}
+        {{ entry.title | search_id | tojson }}: {{ entry.title | entry_id | tojson }},
+        {%- endfor %}
+        {{ section.title | search_id | tojson }}: {{ section.title | section_id | tojson }}{{ "," if not loop.last }}
+        {%- endfor %}
+      }
     </script>
-    <script src="{{root}}/js/manual-search.js"></script>
+    <script src="{{ root }}/js/manual-search.js"></script>
   </body>
 </html>
diff --git a/docs/templates/shared/_footer.html.j2 b/docs/templates/shared/_footer.html.j2
index 511f0b1fac..dbed4f2759 100644
--- a/docs/templates/shared/_footer.html.j2
+++ b/docs/templates/shared/_footer.html.j2
@@ -1,10 +1,14 @@
-    <footer>
-      <div class="container">
-        <p>This website is made with <a href="http://getbootstrap.com">Bootstrap</a>, themed with <a href="https://bootswatch.com">Bootswatch</a>.</p>
-        <p>jq is licensed under the MIT license (code) and the <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY-3.0</a> license (docs).</p>
+    <footer class="bd-footer bg-body-tertiary">
+      <div class="py-3 text-center">
+        <p>
+          This website is made with <a href="https://getbootstrap.com" target="_blank" rel="noopener">Bootstrap</a>,
+          themed with <a href="https://bootswatch.com" target="_blank" rel="noopener">Bootswatch</a>.
+        </p>
+        <p>
+          jq is licensed under the MIT license (code) and the
+          <a href="https://creativecommons.org/licenses/by/3.0/" target="_blank" rel="noopener">CC-BY-3.0</a> license (docs).
+        </p>
       </div>
     </footer>
-
-    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
-    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" integrity="sha256-Sk3nkD6mLTMOF0EOpNtsIry+s1CsaqQC1rVLTAy+0yc= sha512-K1qjQ+NcF2TYO/eI3M6v8EiNYZfA95pQumfvcVrTHtwQVDG+aHRqLi/ETn2uB+1JqwYqVG3LIvdm9lj6imS/pQ==" crossorigin="anonymous"></script>
-    <script src="https://twitter.github.io/typeahead.js/releases/0.11.1/typeahead.bundle.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"
+      integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
diff --git a/docs/templates/shared/_head.html.j2 b/docs/templates/shared/_head.html.j2
index 96c3c4dc60..7a240307f8 100644
--- a/docs/templates/shared/_head.html.j2
+++ b/docs/templates/shared/_head.html.j2
@@ -1,17 +1,23 @@
   <head>
-    <script>
-      if ((window.location.host == "jqlang.github.io") && (window.location.protocol != "https:"))
-        window.location.protocol = "https";
-    </script>
     <meta charset="utf-8">
-    <title>{{headline}}</title>
+    <title>{{ headline }}</title>
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
-    <link rel="canonical" href="{{ url }}/{{ permalink }}" />
-    <link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.5/spacelab/bootstrap.min.css" rel="stylesheet" integrity="sha256-j7Dtnd7ZjexEiPNbscbopFn9+Cs0b3TLipKsWAPHZIM= sha512-RFhfi6P8zWMAJrEGU+CPjuxPh3r/UUBGqQ+/o6WKPIVZmQqeOipGotH2ihRULuQ8wsMBoK15TSZqc/7VYWyuIw==" crossorigin="anonymous">
-    <link rel="stylesheet" href="{{ root }}/css/base.css" type="text/css">
-    <!--[if lt IE 9]>
-      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
-      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
-    <![endif]-->
+    <link rel="icon" href="{{ root }}/icon.svg" type="image/svg+xml">
+    <link rel="apple-touch-icon" href="{{ root }}/icon.png" type="image/png">
+    <link rel="canonical" href="{{ url }}/{{ permalink }}">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.1/dist/cosmo/bootstrap.min.css"
+      integrity="sha384-dulfW0vmzZ638jigSgZXvDxMmd70GCnIv6oa+riKq6Kk4E0MKf7qmBfwP02wltv5" crossorigin="anonymous">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"
+      integrity="sha384-Ay26V7L8bsJTsX9Sxclnvsn+hkdiwRnrjZJXqKmkIDobPgIIWBOVguEcQQLDuhfN" crossorigin="anonymous">
+    <link rel="stylesheet" href="{{ root }}/css/style.css" type="text/css">
+    <script>
+      (function() {
+        function setTheme(mediaQuery) {
+          document.documentElement.setAttribute('data-bs-theme',
+            document.documentElement.style.colorScheme = mediaQuery.matches ? 'dark' : 'light');
+        }
+        const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+        setTheme(mediaQuery); mediaQuery.addEventListener('change', setTheme);
+      })();
+    </script>
   </head>
diff --git a/docs/templates/shared/_navbar.html.j2 b/docs/templates/shared/_navbar.html.j2
index a3238611e7..92222902fe 100644
--- a/docs/templates/shared/_navbar.html.j2
+++ b/docs/templates/shared/_navbar.html.j2
@@ -1,27 +1,40 @@
-    <div class="navbar navbar-default navbar-fixed-top" role="navigation">
-      <div class="container">
-        <div class="navbar-header">
-          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#nav-collapse">
-            <span class="sr-only">Toggle navigation</span>
-            <span class="icon-bar"></span>
-            <span class="icon-bar"></span>
-            <span class="icon-bar"></span>
-          </button>
-          <a class="navbar-brand" href="{{root}}/">jq</a>
-        </div>
-
-        <div class="navbar-collapse collapse" id="nav-collapse">
-          <ul class="nav navbar-nav">
-            {% for item in navigation %}
-              <li {% if item == navitem %} class="active" {% endif %}>
-                <a href="{{root}}/{{item}}/">{{item | capitalize}}</a>
-              </li>
-            {% endfor %}
-            <li><a href="https://github.com/jqlang/jq/issues">Issues</a></li>
-            <li><a href="https://github.com/jqlang/jq">Source</a></li>
-            <li><a href="https://jqplay.org">Try online!</a></li>
-            <li><a href="https://raw.githubusercontent.com/jqlang/jq/master/NEWS.md">News</a></li>
+    <div class="container visually-hidden-focusable">
+      <a class="d-inline-flex p-2" href="#main">Skip to main content</a>
+      {%- if navitem.startswith('manual') %}
+      <a class="d-inline-flex p-2 m-1" href="#contents">Skip to table of contents</a>
+      {%- endif %}
+    </div>
+    <header class="navbar navbar-expand-md sticky-top bg-body-tertiary d-print-none">
+      <nav class="container-lg" aria-label="Page navigation">
+        <button type="button" class="navbar-toggler" data-bs-toggle="offcanvas" data-bs-target="#navbar-collapse"
+          aria-controls="navbar-collapse" aria-expanded="false" aria-label="Toggle page navigation">
+          <span class="navbar-toggler-icon"></span>
+        </button>
+        <a class="navbar-brand" href="{{ root }}/" aria-label="Top page"><img src="{{ root }}/jq.svg" alt="jq logo" style="height:1.5rem"></a>
+        <div id="navbar-collapse" class="offcanvas offcanvas-start navbar-collapse w-auto" aria-labelledby="navbar-title">
+          <div class="offcanvas-header">
+            <h3 id="navbar-title" class="me-3">jq</h3>
+            <button type="button" class="d-md-none btn-close" data-bs-dismiss="offcanvas" aria-label="Close page navigation"></button>
+          </div>
+          <ul class="offcanvas-body navbar-nav me-auto text-nowrap">
+            <li class="nav-item d-md-none"><a class="nav-link{% if not navitem %} active{% endif %}"{% if not navitem %} aria-current="page"{% endif %} href="{{ root }}/">Top page</a></li>
+            {%- for item in ['tutorial', 'download', 'manual'] %}
+            <li class="nav-item"><a class="nav-link{% if item == navitem %} active{% endif %}"{% if item == navitem %} aria-current="page"{% endif %} href="{{ root }}/{{ item }}/">{{ item | capitalize }}</a></li>
+            {%- endfor %}
+            <li class="nav-item"><a class="nav-link" href="https://github.com/jqlang/jq" target="_blank" rel="noopener">GitHub</a></li>
+            <li class="nav-item"><a class="nav-link" href="https://github.com/jqlang/jq/issues" target="_blank" rel="noopener">Issues</a></li>
+            <li class="nav-item"><a class="nav-link" href="https://jqplay.org" target="_blank" rel="noopener">Try online!</a></li>
+            <li class="nav-item"><a class="nav-link" href="https://raw.githubusercontent.com/jqlang/jq/master/NEWS.md" target="_blank" rel="noopener">News</a></li>
           </ul>
         </div>
-      </div>
-    </div>
+        {%- if navitem.startswith('manual') %}
+        <div class="container-searchbox form-control p-0 d-none d-md-flex d-print-none border" style="width:16rem">
+          <span class="bi bi-search my-auto ms-2 me-1" aria-hidden="true"></span>
+          <input type="text" id="searchbox" class="form-control border-0 px-1" placeholder="Search manual" role="combobox"
+            aria-label="Search manual" aria-keyshortcuts="/" aria-expanded="false" autocomplete="off" spellcheck="false">
+          <kbd class="mx-1 my-auto text-body bg-body rounded border" style="padding:.1rem .2rem" aria-hidden="true">/</kbd>
+        </div>
+        {%- endif %}
+        <a class="d-md-none bi bi-github fs-4 text-body" href="https://github.com/jqlang/jq" target="_blank" rel="noopener" aria-label="GitHub"></a>
+      </nav>
+    </header>