diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9017ee569..0b38f132b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,10 +18,10 @@ jobs: matrix: include: - pair: - elixir: "1.11" - otp: "21" + elixir: "1.12" + otp: "22" - pair: - elixir: "1.14" + elixir: "1.16.0-rc.0" otp: "26" lint: lint steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..c091147e4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Release + +on: + push: + tags: + - v* + +permissions: + contents: write + +jobs: + create_release: + continue-on-error: true + runs-on: ubuntu-22.04 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Create release + run: | + echo "Creating release..." + gh release create \ + --repo ${{ github.repository }} \ + --title ${{ github.ref_name }} \ + ${{ github.ref_name }} + + release_pre_built: + needs: create_release + strategy: + fail-fast: true + matrix: + include: + - otp: 24 + otp_version: "24.0" + - otp: 25 + otp_version: "25.0.4" + - otp: 26 + otp_version: "26.0.2" + # - otp: 27 + # otp_version: "27.0" + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 50 + - uses: ./.github/workflows/release_pre_built + with: + otp_version: ${{ matrix.otp_version }} + otp: ${{ matrix.otp }} + + - name: Upload Pre-built + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release upload --clobber "${{ github.ref_name }}" \ + ex_doc_otp_${{ matrix.otp }} \ + ex-doc-otp-${{ matrix.otp }}.sha{1,256}sum \ diff --git a/.github/workflows/release_pre_built/action.yml b/.github/workflows/release_pre_built/action.yml new file mode 100644 index 000000000..f8cc5bd3c --- /dev/null +++ b/.github/workflows/release_pre_built/action.yml @@ -0,0 +1,23 @@ +name: "Release pre built" +description: "Builds ex_doc scripts" +inputs: + otp: + description: "The major OTP version" + otp_version: + description: "The exact OTP version (major.minor[.patch])" +runs: + using: "composite" + steps: + - uses: erlef/setup-beam@v1.16.0 + with: + otp-version: ${{ inputs.otp_version }} + elixir-version: "1.16.0" + - name: Build ex_doc + shell: bash + run: | + mix deps.get + mix escript.build + mv ex_doc ex_doc_otp_${{ inputs.otp }} + shasum -a 1 ex_doc_otp_${{ inputs.otp }} > ex-doc-otp-${{ inputs.otp }}.sha1sum + shasum -a 256 ex_doc_otp_${{ inputs.otp }} > ex-doc-otp-${{ inputs.otp }}.sha256sum + echo "$PWD/bin" >> $GITHUB_PATH diff --git a/CHANGELOG.md b/CHANGELOG.md index cf211c021..b115b5836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,50 @@ # Changelog +## v0.31.0 (2023-12-11) + + * Enhancements + * Allow searching atoms, module attributes, and words finishing with `?` and `!` + * Support upcoming Erlang/OTP 27 documentation format + * Include prebuilt binaries on every release + * Add borders dividing table rows in rendered content + * Add accurate warnings for missing docs from Elixir v1.16+ + * Support `e:dep:some-page.md` for explicitly linking to a page in a package + * Support `m:SomeModule` for explicitly linking to a module + * Add `noindex` meta tag to 404 and Search pages + * Move search to the main content so we can display more results + * Warn when referencing functions, types, and callbacks from filtered out modules + + * Bug fixes + * Fix search for words with hyphens in them + * Fix search for contents inside EEx interpolation + +## v0.30.9 (2023-10-20) + + * Bug fixes + * Fix a scenario where invalid assets would be generated + + * Enhancements + * Add admonition EPUB styles + +## v0.30.8 (2023-10-17) + + * Bug fixes + * Fix regression in umbrella applications + +## v0.30.7 (2023-10-15) + + * Bug fixes + * Do not crash on EDoc type annotations + * Do not crash on functions without name + * Handle remote types in records + * Fix scrolling to top on iOS + * Fix invalid output markup for “hover link” headings + + * Enhancements + * Support any String.Chars as the extra page name + * Improve screen reader accessibility + * Add `:skip_code_autolink_to` option + ## v0.30.6 (2023-08-25) * Enhancements diff --git a/README.md b/README.md index 7ed8f2dd0..a0e24c0aa 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,7 @@ [![Build Status](https://github.com/elixir-lang/ex_doc/workflows/CI/badge.svg)](https://github.com/elixir-lang/ex_doc/actions?query=workflow%3A%22CI%22) -ExDoc is a tool to generate documentation for your Elixir projects. To see an example, [you can access Elixir's official docs](https://hexdocs.pm/elixir/). - -To learn about how to document your projects, see [Elixir's writing documentation page](https://hexdocs.pm/elixir/writing-documentation.html). - -To see all supported options, see the documentation for [mix docs](https://hexdocs.pm/ex_doc/Mix.Tasks.Docs.html). +ExDoc is a tool to generate documentation for Erlang and Elixir projects. To see an example, [you can access Elixir's official docs](https://hexdocs.pm/elixir/). ## Features @@ -30,11 +26,11 @@ ExDoc ships with many features: You can use ExDoc with Mix (recommended for Elixir projects), with Rebar (recommended for Erlang projects), or via the command line. -### Using ExDoc with Mix + -ExDoc requires Elixir v1.10 or later. +### Mix -First, add ExDoc as a dependency: +ExDoc requires Elixir v1.12 or later. Then add ExDoc as a dependency: ```elixir def deps do @@ -74,11 +70,13 @@ end Now you are ready to generate your project documentation with `mix docs`. To see all options available, run `mix help docs`. -### Using ExDoc with Rebar3 +To learn about how to document your projects, see [Elixir's writing documentation page](https://hexdocs.pm/elixir/writing-documentation.html). + +### Rebar3 From Erlang/OTP 24+, you can use ExDoc to render your Erlang documentation written with EDoc. See [`rebar3_ex_doc`](https://github.com/starbelly/rebar3_ex_doc/) for more information. -### Using ExDoc via command line +### CLI You can use ExDoc via the command line. @@ -111,14 +109,30 @@ You can use ExDoc via the command line. GITHUB_REPO => ecto ``` +You can specify a config file via the `--config` option, both Elixir and Erlang formats are supported. Invoke `ex_doc` without arguments to learn more. + + + ## Syntax highlighting ExDoc uses [the makeup project](https://github.com/elixir-makeup/makeup) for syntax highlighting. By default, highlighters for Erlang and Elixir are included. To syntax-highlight other languages, simply add the equivalent `makeup_LANGUAGE` package to your `mix.exs`/`rebar.config` file. For example, for HTML support you would add: + + +### Elixir (Mix) + ```elixir {:makeup_html, ">= 0.0.0", only: :dev, runtime: false} ``` +### Erlang (Rebar3) + +```erlang +{makeup_html, "0.1.1"} +``` + + + You can find all supported languages under [the Makeup organization on GitHub](https://github.com/elixir-makeup) and view them at [Makeup's website](https://elixir-makeup.github.io/makeup_demo/). ## Additional pages @@ -129,20 +143,36 @@ You can publish additional pages in your project documentation by configuring th * Cheatsheets (`.cheatmd` extension) - useful for discovery and quick reference. [Learn more](https://hexdocs.pm/ex_doc/cheatsheet.html). - * Livebooks (`.livemd` extension) - useful for tutorials, interactive examples and deep dives. [Learn more](https://livebook.dev/). + * Livebooks (`.livemd` extension) - useful for tutorials, interactive examples, and deep dives. [Learn more](https://livebook.dev/). For example, you can set your `:extras` to: + + +### Elixir + ```elixir extras: ["README.md", "LICENSE", "tutorial.livemd", "cheatsheet.cheatmd"] ``` Run `mix help docs` for more information on configuration. +### Erlang + +```elixir +{extras, [<<"README.md">>, <<"cheatsheet.cheatmd">>]}. +``` + + + ## Metadata ExDoc supports metadata keys in your documentation. + + +### Elixir + In Elixir, you can add metadata to modules and functions. For a module, use `@moduledoc`, which is equivalent to adding the annotation to everything inside the module (functions, macros, callbacks, types): @@ -157,16 +187,20 @@ For a function, use `@doc`: @doc since: "1.13.1" ``` +### Erlang + In Erlang's EDoc: ```erlang %% @since 0.1.0 ``` + + The following metadata is available for both modules and functions: - * `deprecated` (string) - marks a module/function as deprecated, with the given string as the reason. - * `since` (string) - declares a module/function available from a particular version. + * `deprecated` (binary) - marks a module/function as deprecated, with the given string as the reason. + * `since` (binary) - declares a module/function available from a particular version. The following metadata is available for modules: @@ -174,18 +208,43 @@ The following metadata is available for modules: ## Auto-linking -ExDoc for Elixir will automatically generate links across modules and functions if you enclose them in backticks. +ExDoc for Elixir and Erlang will automatically generate links across modules and functions if you enclose them in backticks. + + + +### Elixir - * When referring to a module, function, type or callback from your project, such as `` `MyModule` ``, ExDoc will automatically link to it. - * When referring to a module, function, type or callback from Elixir, such as `` `String` ``, ExDoc will automatically link to it at Elixir's stable documentation. - * When referring to a function, type, or callback from OTP, such as (`` `:queue.new/0` ``), ExDoc will automatically link to it at the OTP documentation. - * When referring to a module, function, type or callback from any of your dependencies, such as `` `MyDep` ``, ExDoc will automatically link to it at the dependency's documentation at [hexdocs.pm](https://hexdocs.pm/). (The link can be configured by setting `docs: [deps: [my_dep: "https://path/to/docs/"]]` in your `mix.exs`.) +ExDoc will automatically link modules, functions, types or callbacks defined in your project and its dependencies (including Erlang and Elixir). ExDoc will automatically link to it at the dependency's documentation at [hexdocs.pm](https://hexdocs.pm/). The link can be configured by setting `docs: [deps: [my_dep: "https://path/to/docs/"]]` in your `mix.exs`. -ExDoc supports linking to modules (`` `MyModule` ``), functions (`` `MyModule.function/1` ``), types (`` `t:MyModule.type/2` ``) and callbacks (`` `c:MyModule.callback/3` ``). If you want to link a function, type or callback in the current module, you may skip the module name; e.g.: `` `function/1` ``. +ExDoc supports linking to modules (`` `MyModule` `` and `` `m:MyModule` ``), functions (`` `MyModule.function/1` ``), types (`` `t:MyModule.type/2` ``) and callbacks (`` `c:MyModule.callback/3` ``). If you want to link a function, type or callback in the current module, you may skip the module name, for example: `` `function/1` ``. -You can also use custom text; e.g.: `` [custom text](`MyModule.function/1`) ``. This also allows you to refer to OTP modules; e.g.: `` [`:array`](`:array`) ``. +You can also use custom text, such as `` [custom text](`MyModule.function/1`) ``. Link to extra pages using the syntax `` [Up and running](Up and running.md) ``. The final link will be automatically converted to `up-and-running.html`. -Link to extra pages using the syntax `` [Up and running](Up and running.md) ``, skipping the directory in which the page is. The final link will be automatically converted to `up-and-running.html`. +Link to extra pages in another application using the syntax `` [Writing Documentation](`e:elixir:writing-documentation.html`) ``, skipping the directory in which the page is. The final link will be automatically converted to `https://hexdocs.pm/elixir/writing-documentation.html`. + +It is also possible to place anchors after the module name and extra pages. For example: + +* `` `Keyword#module-duplicate-keys-and-ordering` `` will create a link to `https://hexdocs.pm/elixir/Keyword.html#module-duplicate-keys-and-ordering` +* `` `e:elixir:syntax-reference.md#expressions` `` will create a link to `https://hexdocs.pm/elixir/syntax-reference.html#expressions` + +### Erlang + +ExDoc will automatically link modules, functions, types or callbacks defined in your project and its dependencies (including Erlang and Elixir). ExDoc will automatically link to it at the dependency's documentation at [hexdocs.pm](https://hexdocs.pm/). The link can be configured by setting `docs: [deps: [my_dep: "https://path/to/docs/"]]` in your `mix.exs`. The link can be configured by setting `{docs, [{deps, [{my_dep, "https://path/to/docs/"}]}]}` in your `rebar3.config`. + +ExDoc supports linking to modules (`` `m:my_module` ``), functions (`` `my_module:function/1` ``), types (`` `t:my_module:type/2` ``) and callbacks (`` `c:my_module:callback/3` ``). If you want to link a function, type or callback in the current module, you may skip the module name; e.g.: `` `function/1` ``. + +You can also use custom text, such as `` [custom text](`my_module:function/1`) ``. This also allows you to refer to Erlang/OTP modules: `` [The array module](`array`) `` (note that when a module is given as the link target, it is not necessary nor possible to use the `m:` prefix). + +Link to extra pages using the syntax `` [Up and running](Up and running.md) ``. The final link will be automatically converted to `up-and-running.html`. + +Link to extra pages in another application using the syntax `` [Using unicode](`e:stdlib:unicode_usage.html`) ``, skipping the directory in which the page is. The final link will be automatically converted to `https://hexdocs.pm/elixir/writing-documentation.html`. + +It is also possible to place anchors after the module name and extra pages. For example: + +* `` `m:argparse#quick-start` `` will create a link to `https://erlang.org/doc/man/argparse#quick-start` +* `` `e:stdlib:unicode-usage.md#what-unicode-is` `` will create a link to `https://erlang.org/doc/apps/stdlib/unicode-usage.html#what-unicode-is` + + ## Admonition blocks diff --git a/assets/css/_epub.css b/assets/css/_epub.css index ca7ec9fbf..dd5232268 100644 --- a/assets/css/_epub.css +++ b/assets/css/_epub.css @@ -1,6 +1,7 @@ @import 'custom-props/common.css'; @import 'custom-props/theme-light.css'; +@import 'content/epub-admonition.css'; @import 'content/code.css'; @import 'content/functions.css'; @import 'screen-reader.css'; diff --git a/assets/css/_html.css b/assets/css/_html.css index 374bd48e7..7c8072bf1 100644 --- a/assets/css/_html.css +++ b/assets/css/_html.css @@ -15,6 +15,7 @@ @import 'icons.css'; @import 'layout.css'; @import 'sidebar.css'; +@import 'search-bar.css'; @import 'focus.css'; @import 'content/general.css'; @import 'content/admonition.css'; diff --git a/assets/css/autocomplete.css b/assets/css/autocomplete.css index e27ed7571..6fb1c86eb 100644 --- a/assets/css/autocomplete.css +++ b/assets/css/autocomplete.css @@ -1,10 +1,51 @@ .autocomplete { display: none; height: 0; - margin: 0 5px 0 12px; + margin-right: 32px; overflow: visible; position: relative; - width: 100%; +} + +.autocomplete .triangle { + width: 0; + height: 0; + border-left: 12px solid transparent; + border-right: 12px solid transparent; + border-bottom: 12px solid var(--autocompleBackground); + position: absolute; + top: 9px; + left: 10%; + transform: translateX(-50%); + z-index: 100; + background-color: transparent; +} + +.autocomplete-results { + list-style: none; + margin: 0; + padding: 15px; + display: flex; + justify-content: space-between; + color: var(--autocompleteResults); + font-family: var(--sansFontFamily); + font-weight: 300; + font-size: 0.9rem; + gap: 3%; +} + +.autocomplete-results .show { + white-space: normal; + overflow-x: hidden; + width: 62%; +} + +.autocomplete-results .bold { + color: var(--autocompleteResultsBold); + font-weight: 400; +} + +.autocomplete-results .mobile-hide { + width: 35%; } .autocomplete.shown { @@ -12,27 +53,33 @@ } .autocomplete-suggestions { - box-shadow: 2px 2px 10px rgba(0,0,0,.25); - background-color: var(--gray700); - border-top: 1px solid var(--gray800); - left: 0; + background-color: var(--autocompleBackground); + border-radius: 8px; position: absolute; - top: -2px; - width: 276px; + top: 15px; + width: 100%; z-index: 200; + box-shadow: 0px 15px 99px 0px var(--autocompleteBorder); + overflow-y: auto; + max-height: 450px; + padding: 0 0 10px 0; + white-space: normal; + overflow-x: hidden; } .autocomplete-suggestion { color: inherit; display: block; - padding: 10px; + padding: 12px 20px; text-decoration: none; + transition: background-color 0.3s ease-in-out; + border-bottom: 1px solid var(--suggestionBorder); + font-size: 0.9rem; } .autocomplete-suggestion:hover, .autocomplete-suggestion.selected { - background-color: var(--gray600); - border-left: 3px solid var(--main); + background-color: var(--autocompleteHover); } .autocomplete-suggestion em { @@ -41,13 +88,20 @@ } .autocomplete-suggestion .description { - opacity: .6; + opacity: 0.6; padding-top: 3px; } .autocomplete-suggestion .label { - padding-left: 2px; - opacity: .75; + background-color: var(--autocompleteLabelBack); + opacity: 0.6; + color: var(--autocompleteLabelFont); + padding: 4px 8px 4px 8px; + border-radius: 4px; + margin-left: 10px; + text-transform: uppercase; + font-family: var(--sansFontFamily); + font-size: 0.7rem; } .autocomplete-suggestion .title, @@ -57,3 +111,51 @@ white-space: nowrap; width: 100%; } + +.autocomplete-suggestions a { + text-decoration: none !important; +} + +/* Style the scrollbar track (the area behind the thumb) */ +.autocomplete-suggestions::-webkit-scrollbar { + width: 5px; /* Set the width of the scrollbar */ + border-radius: 7px; /* Add rounded corners to the thumb */ + flex-shrink: 0; +} + +/* Style the scrollbar thumb (the draggable part) */ +.autocomplete-suggestions::-webkit-scrollbar-thumb { + background-color: var(--autocompleteSrollbarThumb); + border-radius: 7px; +} + +/* Style the scrollbar track on hover */ +.autocomplete-suggestions::-webkit-scrollbar-track { + background-color: var(--autocompleteSrollbarTrack); +} + +@media (max-width: 480px) { + .autocomplete .triangle { + left: 27%; + } +} + +@media (max-width: 767px) { + .autocomplete .triangle { + left: 17%; + } +} + +@media (max-width: 1024px) { + .autocomplete-results { + gap: 0; + } + + .autocomplete-results .show { + width: 100%; + } + + .autocomplete-results .mobile-hide { + display: none; + } +} diff --git a/assets/css/content/cheatsheet.css b/assets/css/content/cheatsheet.css index 4c6b81155..dacd19a30 100644 --- a/assets/css/content/cheatsheet.css +++ b/assets/css/content/cheatsheet.css @@ -37,18 +37,14 @@ .page-cheatmd .content-inner h3 { color: var(--main); + text-decoration-color: var(--main); margin: 0 0 1em; font-weight: 400; - overflow: hidden; } .page-cheatmd .content-inner h3 :is(a, a:visited) { color: var(--main); - text-decoration: none; -} - -.page-cheatmd .content-inner h3.section-heading i { - display: none; + text-decoration-color: var(--main); } .page-cheatmd .content-inner section.h3 { @@ -58,7 +54,11 @@ break-inside: avoid; } -.page-cheatmd .content-inner h3::after { +.page-cheatmd .content-inner h3 .text { + overflow: hidden; /* Clips generated content horizontal rule */ +} + +.page-cheatmd .content-inner h3 .text::after { content: ""; margin-left: calc(var(--horizontal-space) / 2); vertical-align: baseline; diff --git a/assets/css/content/epub-admonition.css b/assets/css/content/epub-admonition.css new file mode 100755 index 000000000..45f4a05ca --- /dev/null +++ b/assets/css/content/epub-admonition.css @@ -0,0 +1,76 @@ +.content-inner blockquote:is(.warning, .error, .info, .neutral, .tip) { + border-left: solid 4px; + color: var(--black); + font-size: 0.9em; + line-height: 1.4em; + margin-bottom: 1.5em; + margin-left: 5px; + padding: 7px 15px; + page-break-inside: avoid; +} + +.content-inner blockquote.warning { + background-color: var(--warningBackground); + border-left-color: var(--warningHeadingBackground); +} + +.content-inner blockquote.error { + background-color: var(--errorBackground); + border-left-color: var(--errorHeadingBackground); +} + +.content-inner blockquote.info { + background-color: var(--infoBackground); + border-left-color: var(--infoHeadingBackground); +} + +.content-inner blockquote.neutral { + background-color: var(--neutralBackground); + border-left-color: var(--neutralHeadingBackground); +} + +.content-inner blockquote.tip { + background-color: var(--tipBackground); + border-left-color: var(--tipHeadingBackground); +} + +.content-inner blockquote :is(h3, h4) { + font-weight: bold; + margin: 0px 10px 5px 0px; +} + +.content-inner blockquote :is(h3, h4):is(.warning, .error, .info, .neutral, .tip) { + font-style: normal; + font-weight: 700; +} + +.content-inner blockquote :is(h3, h4).warning { + color: var(--warningHeadingBackground); +} +.content-inner blockquote :is(h3, h4).error { + color: var(--errorHeadingBackground); +} +.content-inner blockquote :is(h3, h4).info { + color: var(--infoHeadingBackground); +} +.content-inner blockquote :is(h3, h4).neutral { + color: var(--neutralHeadingBackground); +} +.content-inner blockquote :is(h3, h4).tip { + color: var(--tipHeadingBackground); +} + +.content-inner blockquote :is(h3, h4):is(.warning, .error, .info, .neutral, .tip) code { + margin: 0 0.5ch; +} + +.content-inner blockquote:is(.warning, .error, .info, .neutral, .tip) code { + background-color: var(--admInlineCodeBackground); + border: 1px solid var(--admInlineCodeBorder); + color: var(--admInlineCode); +} + +.content-inner blockquote:is(.warning, .error, .info, .neutral, .tip) pre code { + background-color: var(--admCodeBackground); + border: 1px solid var(--admCodeBorder); +} diff --git a/assets/css/content/general.css b/assets/css/content/general.css index ca47e16c9..dc9750378 100644 --- a/assets/css/content/general.css +++ b/assets/css/content/general.css @@ -109,6 +109,7 @@ .content-inner table { margin: 2em 0; + border-collapse: collapse; } .content-inner th { @@ -119,42 +120,74 @@ padding-bottom: .5em; } +.content-inner thead tr { + border-bottom: 1px solid var(--tableHeadBorder); +} + +.content-inner tbody tr { + border-bottom: 1px solid var(--tableBodyBorder); +} + +.content-inner tbody tr:last-child { + border-bottom: none +} + .content-inner tr { - border-bottom: 1px solid var(--gray50); vertical-align: bottom; height: 2.5em; } .content-inner :is(td, th) { + padding: 0.25em; padding-left: 1em; line-height: 2em; vertical-align: top; } -.content-inner .section-heading a { +.content-inner .section-heading { + --icon-size: 16px; + --icon-spacing: 5px; + display: grid; + grid-template: 1fr / 1fr; +} + +.content-inner .section-heading > :is(.hover-link, .text) { + grid-row: 1; + grid-column: 1; +} + +.content-inner .section-heading .hover-link { text-decoration: none; } .content-inner .section-heading i { - font-size: 16px; + font-size: var(--icon-size); margin-top: .1em; - margin-left: -21px; + margin-left: calc(-1 * (var(--icon-size) + var(--icon-spacing))); + padding-right: var(--icon-spacing); /* Avoids gap in hover area */ opacity: 0; } -.content-inner .section-heading a:is(:hover, :focus) i { - opacity: 1; +@media screen and (max-width: 768px) { + .content-inner .section-heading i { + margin-left: calc(-1 * (var(--icon-size))); + } } -blockquote .section-heading i { +.content-inner blockquote .section-heading i { display: none; } -@media screen and (max-width: 768px) { - .content-inner .section-heading i { - margin-left: -16px; - margin-right: -5px; - } +.content-inner .section-heading .hover-link:is(:hover, :focus) i { + opacity: 1; +} + +/* Allow section link to be hovered and used “through” text */ +.content-inner .section-heading .text { + pointer-events: none; +} +.content-inner .section-heading .text a { + pointer-events: all; } .content-inner .app-vsn { diff --git a/assets/css/copy-button.css b/assets/css/copy-button.css index b90431f88..3cda31f29 100644 --- a/assets/css/copy-button.css +++ b/assets/css/copy-button.css @@ -37,13 +37,9 @@ pre .copy-button:hover svg, pre .copy-button:focus-visible svg { } .copy-button.clicked { - display: block; opacity: 1; color: var(--success); } -.copy-button.clicked::after { - content: "Copied! \2713"; -} .copy-button.clicked svg { display: none; diff --git a/assets/css/custom-props/_elixir.css b/assets/css/custom-props/_elixir.css index 8088a49dd..c9b0c3dd0 100644 --- a/assets/css/custom-props/_elixir.css +++ b/assets/css/custom-props/_elixir.css @@ -4,4 +4,6 @@ --main-darkened-20: hsl(250, 68%, 49%); --main-lightened-05: hsl(250, 68%, 74%); --main-lightened-10: hsl(250, 68%, 79%); + --searchBarFocusColor: #8E7CE6; + --searchBarBorderColor: rgba(142, 124, 230, 0.25); } diff --git a/assets/css/custom-props/_erlang.css b/assets/css/custom-props/_erlang.css index 330259475..cb91cf2c0 100644 --- a/assets/css/custom-props/_erlang.css +++ b/assets/css/custom-props/_erlang.css @@ -4,4 +4,6 @@ --main-darkened-20: hsl(0, 100%, 44%); --main-lightened-05: hsl(0, 100%, 69%); --main-lightened-10: hsl(0, 100%, 74%); + --searchBarFocusColor: hsl(0, 100%, 70%); + --searchBarBorderColor: rgb(255,71,71, 0.1); } diff --git a/assets/css/custom-props/common.css b/assets/css/custom-props/common.css index d65b0c35d..1a66c47e1 100644 --- a/assets/css/custom-props/common.css +++ b/assets/css/custom-props/common.css @@ -3,6 +3,7 @@ --content-width: 949px; --content-gutter: 60px; --borderRadius: 4px; + --navTabBorderWidth: 4px; /* Font Families */ --serifFontFamily: 'Merriweather', 'Book Antiqua', Georgia, 'Century Schoolbook', serif; @@ -20,6 +21,7 @@ --gray200: hsl(210, 29%, 88% ); --gray300: hsl(210, 26%, 84% ); --gray400: hsl(210, 21%, 64% ); + --gray450: hsl(210, 21%, 49% ); --gray500: hsl(210, 21%, 34% ); --gray600: hsl(210, 27%, 26% ); --gray700: hsl(212, 35%, 17% ); diff --git a/assets/css/custom-props/theme-dark.css b/assets/css/custom-props/theme-dark.css index 887f8f537..49e978bc6 100644 --- a/assets/css/custom-props/theme-dark.css +++ b/assets/css/custom-props/theme-dark.css @@ -10,7 +10,7 @@ body.dark { --linksVisited: var(--gray100); --linksNoUnderline: var(--main-lightened-10); --linksNoUnderlineVisited: var(--main-lightened-05); - --linksDecoration: var(--gray500); + --linksDecoration: var(--gray450); --iconAction: var(--coldGray-lightened-10); --iconActionHover: var(--white); @@ -18,6 +18,9 @@ body.dark { --blockquoteBackground: var(--coldGrayDim); --blockquoteBorder: var(--coldGrayDark); + --tableHeadBorder: var(--gray600); + --tableBodyBorder: var(--gray700); + --warningBackground: hsl( 40, 67%, 79%); --warningHeadingBackground: hsl( 27, 66%, 29%); --warningHeading: var(--white); @@ -80,10 +83,23 @@ body.dark { --sidebarHover: var(--white); --sidebarScrollbarThumb: var(--coldGray); --sidebarScrollbarTrack: var(--sidebarBackground); - --sidebarSearch: var(--gray700); --sidebarSubheadings: var(--gray400); --sidebarItem: var(--gray200); --sidebarInactiveItemMarker: var(--gray600); --sidebarLanguageAccentBar: var(--main); --sidebarActiveItem: var(--main-lightened-10); + --searchBarBorder: var(--gray500); + --searchAccentMain: var(--gray300); + --searchLanguageAccentBar: var(--main); + --searchSearch: var(--gray900); + --autocompleteBorder: rgba(0,0,0,.25); + --autocompleteHover: var(--gray700); + --autocompleBackground: var(--gray800); + --suggestionBorder: var(--gray600); + --autocompleteResults: var(--gray200); + --autocompleteResultsBold: var(--gray100); + --autocompleteSrollbarThumb: var(--gray600); + --autocompleteSrollbarTrack: var(--gray850); + --autocompleteLabelBack: var(--gray600); + --autocompleteLabelFont: rgba(255, 255, 255, 0.8); } diff --git a/assets/css/custom-props/theme-light.css b/assets/css/custom-props/theme-light.css index 608644ab9..31e86eddc 100644 --- a/assets/css/custom-props/theme-light.css +++ b/assets/css/custom-props/theme-light.css @@ -10,7 +10,7 @@ --linksVisited: var(--black); --linksNoUnderline: var(--main-darkened-10); --linksNoUnderlineVisited: var(--main-darkened-20); - --linksDecoration: var(--gray400); + --linksDecoration: var(--gray450); --iconAction: var(--coldGray); --iconActionHover: var(--gray800); @@ -18,6 +18,9 @@ --blockquoteBackground: var(--coldGrayFaint); --blockquoteBorder: var(--coldGrayLight); + --tableHeadBorder: var(--gray100); + --tableBodyBorder: var(--gray50); + --warningBackground: hsl( 33, 100%, 97%); --warningHeadingBackground: hsl( 33, 87%, 64%); --warningHeading: var(--black); @@ -80,10 +83,23 @@ --sidebarHover: var(--white); --sidebarScrollbarThumb: var(--coldGray); --sidebarScrollbarTrack: var(--sidebarBackground); - --sidebarSearch: var(--gray700); --sidebarSubheadings: var(--gray400); --sidebarItem: var(--gray200); --sidebarInactiveItemMarker: var(--gray600); --sidebarLanguageAccentBar: var(--main); --sidebarActiveItem: var(--main-lightened-10); + --searchBarBorder: var(--gray200); + --searchAccentMain: var(--gray-400); + --searchLanguageAccentBar: var(--main); + --searchSearch: var(--white); + --autocompleteBorder: rgba(3, 9, 19, 0.10); + --autocompleteHover: var(--grey50, #F0F5F9); + --autocompleBackground: var(--white); + --suggestionBorder: var(--gray200); + --autocompleteResults: var(--gray500); + --autocompleteResultsBold: var(--gray700); + --autocompleteSrollbarThumb: var(--gray200); + --autocompleteSrollbarTrack: var(--gray50); + --autocompleteLabelBack: var(--gray100); + --autocompleteLabelFont: var(--gray500); } diff --git a/assets/css/layout.css b/assets/css/layout.css index 6f68003ff..d889a000b 100644 --- a/assets/css/layout.css +++ b/assets/css/layout.css @@ -7,7 +7,7 @@ body { body { --sidebarWidth: 300px; - --sidebarTransitionDuration: .3s; + --sidebarTransitionDuration: 0.3s; background-color: var(--background); color: var(--textBody); font-size: 16px; @@ -39,14 +39,13 @@ body { } .sidebar-button { - --sidebarButtonTopOpen: 6px; - --sidebarButtonRightOpen: 7px; + --sidebarButtonRightOpen: 4px; position: fixed; z-index: 99; left: 0; - top: 0; transition: all var(--sidebarTransitionDuration) ease-in-out; will-change: transform; + margin-top: 12px; } .content { @@ -57,16 +56,10 @@ body { z-index: 3; } -@media screen and (max-width: 768px) { - .content { - overflow: auto; - scroll-padding-top: 45px; - } -} - body:is(.sidebar-opening, .sidebar-opened) .sidebar-button { - transform: translateX(calc(var(--sidebarWidth) - 100% - var(--sidebarButtonRightOpen))); - top: var(--sidebarButtonTopOpen); + transform: translateX( + calc(var(--sidebarWidth) - 100% - var(--sidebarButtonRightOpen)) + ); } body.sidebar-opening-start .sidebar { @@ -114,10 +107,31 @@ body.sidebar-closed .content { left: 0; } +.content-inner { + max-width: var(--content-width); + margin: 0 auto; + padding: 3px var(--content-gutter); +} + +.content-inner:focus { + outline: none; +} + +.content-outer { + min-height: 100%; +} + @media screen and (max-width: 768px) { + + .sidebar-button { + position: absolute; + top: 4px; + left: -1px; + } + .sidebar-button:before { --sidebarButtonHeight: 60px; - content: ''; + content: ""; display: block; z-index: -1; pointer-events: none; @@ -126,7 +140,6 @@ body.sidebar-closed .content { top: 0; height: var(--sidebarButtonHeight); width: calc(100vw + 2px); - background: var(--sidebarButtonBackground); transition: top var(--sidebarTransitionDuration) ease-in-out; } @@ -143,29 +156,9 @@ body.sidebar-closed .content { } } -body.search-focused .sidebar-button { - transform: translateX(250px) scaleY(0); - transition: all .15s ease-out; - opacity: 0; -} - -body.search-focused .sidebar-search .search-close-button { +body.search-focused .search-bar .search-close-button { transform: scaleY(1); - transition: transform .15s ease-out .15s; -} - -.content-inner { - max-width: var(--content-width); - margin: 0 auto; - padding: 3px var(--content-gutter); -} - -.content-inner:focus { - outline: none; -} - -.content-outer { - min-height: 100%; + transition: transform 0.15s ease-out 0.15s; } @media screen and (max-width: 768px) { diff --git a/assets/css/search-bar.css b/assets/css/search-bar.css new file mode 100644 index 000000000..7410c931b --- /dev/null +++ b/assets/css/search-bar.css @@ -0,0 +1,125 @@ +.top-search { + margin-top: 10px; +} + +.search-settings { + display: flex; + column-gap: 12px; + align-items: center; +} + +.search-bar { + border: 1px solid var(--searchBarBorder); + border-radius: 8px; + height: 48px; + position: relative; + flex-grow: 1; +} + +.top-search .search-bar .search-input { + background-color: var(--searchSearch); + border: none; + border-radius: 8px; + color: var(--searchAccentMain); + position: relative; + height: 46px; + padding: 8px 35px 8px 43px; + width: 100%; +} + +.top-search .search-bar .search-input::placeholder { + color: var(--searchAccentMain); + opacity: 0.5; +} + +.top-search .search-bar .search-input:focus { + border: 1px solid var(--searchBarFocusColor); + border-radius: 8px; + height: 48px; + position: relative; + box-shadow: 0px 4px 20px 0px var(--searchBarBorderColor) inset; +} + +.top-search .search-bar .search-label { + position: relative; +} + +.top-search .search-bar .search-button { + font-size: 14px; + color: var(--searchAccentMain); + background-color: transparent; + border: none; + cursor: pointer; + left: 11px; + opacity: .5; + padding: 5px 1px 5px 5px; + position: absolute; + top: 60%; + transform: translateY(-60%); + z-index: 2; +} + +.top-search .search-bar.selected .search-button, +.top-search .search-bar .search-button:hover, +.top-search .search-bar .search-button:focus { + color: var(--top-searchLanguageAccentBar); + opacity: 1; +} + +.top-search .search-bar .search-close-button { + font-size: 16px; + color: var(--searchAccentMain); + background-color: transparent; + border: none; + cursor: pointer; + right: 11px; + margin: 0; + opacity: .5; + padding: 5px 1px 5px 0; + position: absolute; + transform: scaleY(0); + top: calc(50% - 11px); + transition: .15s transform ease-out; + z-index: 2; +} + +.top-search .search-bar .search-close-button:hover { + opacity: .7; +} + +.top-search .search-settings button.icon-settings { + display: flex; + align-items: center; + justify-content: flex-end; +} + +.top-search .search-settings .icon-settings { + font-size: 20px; + float: right; + color: var(--iconAction); + text-decoration: none; + border: none; + transition: color .3s ease-in-out; + background-color: transparent; + cursor: pointer; + padding: 0; +} + +.top-search .search-settings .icon-settings:hover { + color: var(--iconActionHover); +} +.top-search .search-settings .icon-settings:visited { + color: var(--iconAction); +} + +@media (max-width: 480px) or ((min-width: 481px) and (max-width: 768px)) { + .search-bar { + margin-left: 35px; + } +} + +@media (max-width: 768px) { + .top-search { + margin-top: 0; + } +} diff --git a/assets/css/search.css b/assets/css/search.css index cefeb7f56..3ecf1e8d0 100644 --- a/assets/css/search.css +++ b/assets/css/search.css @@ -6,10 +6,9 @@ #search .loading { height: 64px; width: 64px; - vertical-align: middle; position: absolute; top: 50%; - left: calc(18%); + left: calc(50% - 32px); } #search .loading div { diff --git a/assets/css/sidebar.css b/assets/css/sidebar.css index 9a096eba6..69f380ed3 100644 --- a/assets/css/sidebar.css +++ b/assets/css/sidebar.css @@ -27,7 +27,7 @@ .sidebar a { color: var(--sidebarAccentMain); text-decoration: none; - transition: color .3s ease-in-out; + transition: color 0.3s ease-in-out; } .sidebar a:hover { @@ -35,25 +35,26 @@ } .sidebar .sidebar-header { - margin: 12px; - border-radius: var(--borderRadius); background-color: var(--sidebarHeader); - width: 276px; + width: 100%; } -.sidebar .sidebar-projectDetails { - display: inline-block; - text-align: left; - vertical-align: top; - margin: 6px 0 0 10px; +.sidebar .sidebar-projectInfo { + display: flex; + justify-content: start; + align-items: center; + gap: 16px; + margin: 12px 16px 12px 14px; + max-width: 235px; } .sidebar .sidebar-projectImage { - display: inline-block; + align-self: flex-end; +} + +.sidebar .sidebar-projectImage img { max-width: 48px; max-height: 48px; - margin: 0 0 0 10px; - vertical-align: bottom; } .sidebar .sidebar-projectName { @@ -94,11 +95,10 @@ color: initial; } -.sidebar .sidebar-projectVersion form::after { +.sidebar .sidebar-projectVersionsDropdownCaret { position: absolute; left: 0; top: 2px; - content: "\25bc"; z-index: 1; font-size: 8px; color: var(--sidebarMuted); @@ -109,13 +109,14 @@ } .sidebar .sidebar-listNav { - padding: 0; - padding-top: 12px; + display: flex; margin: 0; + padding: 4px 4px 0 4px; } -.sidebar .sidebar-listNav :is(li, li a) { +.sidebar .sidebar-listNav :is(li, li button) { text-transform: uppercase; + letter-spacing: 0.02em; font-size: 14px; color: var(--sidebarMuted); } @@ -125,124 +126,74 @@ padding: 0; } -.sidebar .sidebar-listNav li a { +.sidebar .sidebar-listNav li button { + background: none; + border: 0; + border-radius: 0; + -webkit-appearance: none; + text-align: inherit; + color: inherit; + font-weight: inherit; + cursor: pointer; display: inline-block; line-height: 27px; - border-bottom: 3px solid transparent; - padding: 0 10px; -} - -.sidebar .sidebar-listNav li:is(:hover, .selected) a { - border-color: var(--sidebarLanguageAccentBar); -} - -.sidebar .sidebar-listNav li:is(:hover, .selected) a { - color: var(--sidebarAccentMain); -} - -.sidebar .sidebar-search { - margin-top: 12px; -} - -.sidebar .sidebar-search.selected .search-button, -.sidebar .sidebar-search .search-button:hover, -.sidebar .sidebar-search .search-button:focus { - color: var(--sidebarLanguageAccentBar); - opacity: 1; -} - -.sidebar .sidebar-search .search-label { - position: relative; - width: 100%; -} - -.sidebar .sidebar-search .search-button { - font-size: 14px; - color: var(--sidebarAccentMain); - background-color: transparent; - border: none; - cursor: pointer; - left: 22px; - margin: 0; - opacity: .5; - padding: 3px 1px 3px 0; - position: absolute; - top: 18px; - z-index: 2; -} - -.sidebar .sidebar-search .search-close-button { - font-size: var(--sidebarFontSize); - color: var(--sidebarAccentMain); - background-color: transparent; - border: none; - cursor: pointer; - right: 18px; - margin: 0; - opacity: .5; - padding: 5px 1px 5px 0; - position: absolute; - transform: scaleY(0); - top: 17px; - transition: .15s transform ease-out; - z-index: 2; + padding: 4px 10px 2px 10px; + transition: all 150ms; } -.sidebar .sidebar-search .search-close-button:hover { - opacity: .7; +.sidebar .sidebar-listNav li:is(.selected) button { + background-color: var(--sidebarBackground); + border-top: var(--navTabBorderWidth) solid var(--sidebarLanguageAccentBar); } -.sidebar .sidebar-search .search-input { - background-color: var(--sidebarSearch); - border: none; - border-radius: var(--borderRadius); - color: var(--sidebarAccentMain); - margin-left: 12px; - padding: 8px 6px 8px 38px; - width: 276px; +.sidebar .sidebar-listNav li:not(.selected) button { + border-top: var(--navTabBorderWidth) solid var(--sidebarHeader); } -.sidebar .sidebar-search .search-input::placeholder { +.sidebar .sidebar-listNav li:is(:hover):not(.selected) button { + background-color: var(--gray600); + border-top: var(--navTabBorderWidth) solid var(--gray400); color: var(--sidebarAccentMain); - opacity: 0.3; -} - -.sidebar .sidebar-search .ri-search-2-line { - font-weight: bold; + transition: all 150ms; } -.sidebar #full-list { - margin: 0; - padding: 20px 0; +.sidebar .sidebar-tabpanel { + flex: 1 1 0.01%; overflow-y: auto; overscroll-behavior: contain; position: relative; -webkit-overflow-scrolling: touch; - flex: 1 1 .01%; + margin-top: 12px; } -.sidebar #full-list :is(li, a) { +.sidebar .full-list { + margin: 0; + padding: 0 0 20px 0; + position: relative; +} + +.sidebar .full-list :is(li, a) { overflow: hidden; text-overflow: ellipsis; } -.sidebar #full-list li { +.sidebar .full-list li { padding: 0; margin-right: 30px; line-height: 27px; white-space: nowrap; } -.sidebar #full-list li.docs { +.sidebar .full-list li.docs { margin-right: 0; } -.sidebar #full-list li.open > ul { +.sidebar .full-list li.open > ul { display: block; margin-left: 10px; } -.sidebar #full-list li a.expand + button.icon-expand { +.sidebar .full-list li a.expand + button.icon-expand { appearance: none; background-color: transparent; border: 0; @@ -258,106 +209,106 @@ transform: translateY(calc(-100% - 4px)); } -.sidebar #full-list li a + button.icon-expand:after { +.sidebar .full-list li a + button.icon-expand:after { font-family: remixicon; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -.sidebar #full-list li a.expand + button.icon-expand:after { +.sidebar .full-list li a.expand + button.icon-expand:after { content: var(--icon-arrow-down-s); } -.sidebar #full-list li.open > a.expand + button.icon-expand:after { +.sidebar .full-list li.open > a.expand + button.icon-expand:after { content: var(--icon-arrow-up-s); } -.sidebar #full-list li.docs > a + button.icon-expand { +.sidebar .full-list li.docs > a + button.icon-expand { margin-right: 12px; font-size: var(--sidebarFontSize); line-height: var(--sidebarFontSize); transform: translateY(calc(-100% - 5px)); } -.sidebar #full-list li.docs > a + button.icon-expand:after { +.sidebar .full-list li.docs > a + button.icon-expand:after { content: var(--icon-add); } -.sidebar #full-list li.docs.open > a + button.icon-expand:after { +.sidebar .full-list li.docs.open > a + button.icon-expand:after { content: var(--icon-subtract); } -.sidebar #full-list li.nesting-context { +.sidebar .full-list li.nesting-context { font-weight: bold; - font-size: .9em; + font-size: 0.9em; line-height: 1.8em; color: var(--sidebarSubheadings); padding-left: 15px; } -.sidebar #full-list li.group { +.sidebar .full-list li.group { text-transform: uppercase; font-weight: bold; - font-size: .8em; + font-size: 0.8em; margin: 1.5em 0 0; line-height: 1.8em; color: var(--sidebarSubheadings); padding-left: 15px; } -.sidebar #full-list li a { +.sidebar .full-list li a { padding: 3px 0 3px 15px; color: var(--sidebarItem); } -.sidebar #full-list > li > a { +.sidebar .full-list > li > a { display: block; width: 100%; height: 27px; line-height: var(--sidebarLineHeight); } -.sidebar #full-list li .current-section > a { +.sidebar .full-list li .current-section > a { color: var(--sidebarActiveItem); } -.sidebar #full-list li .current-section > a + button.icon-expand { +.sidebar .full-list li .current-section > a + button.icon-expand { color: var(--sidebarActiveItem); } -.sidebar #full-list > li > a:hover { +.sidebar .full-list > li > a:hover { border-left: 3px solid var(--sidebarLanguageAccentBar); padding-left: 12px; } -.sidebar #full-list > li.current-page > a { +.sidebar .full-list > li.current-page > a { color: var(--sidebarActiveItem); border-left: 3px solid var(--sidebarLanguageAccentBar); padding-left: 12px; } -.sidebar #full-list > li.current-page > a:after, -.sidebar #full-list > li.current-page { +.sidebar .full-list > li.current-page > a:after, +.sidebar .full-list > li.current-page { color: var(--sidebarActiveItem); } -.sidebar #full-list > li:last-child { +.sidebar .full-list > li:last-child { margin-bottom: 30px; } -.sidebar #full-list > li.group:first-child { +.sidebar .full-list > li.group:first-child { margin-top: 0; } -.sidebar #full-list ul { +.sidebar .full-list ul { display: none; margin: 10px 15px; margin-right: 0; padding: 0; } -.sidebar #full-list ul li { +.sidebar .full-list ul li { font-weight: 300; line-height: var(--sidebarFontSize); padding: 0 8px; @@ -365,65 +316,65 @@ color: var(--sidebarAccentMain); } -.non-apple-os .sidebar #full-list ul li { +.non-apple-os .sidebar .full-list ul li { font-weight: 400; /* Non-Apple OSes render small light type too thinly */ } -.sidebar #full-list ul li.current-hash { +.sidebar .full-list ul li.current-hash { color: var(--sidebarActiveItem); } -.sidebar #full-list ul li.current-hash > a { +.sidebar .full-list ul li.current-hash > a { color: var(--sidebarActiveItem); } -.sidebar #full-list ul li.current-hash > a:before, -.sidebar #full-list > li > ul > li > a:hover:before { +.sidebar .full-list ul li.current-hash > a:before, +.sidebar .full-list > li > ul > li > a:hover:before { content: "\2022"; position: absolute; margin-left: -15px; color: var(--sidebarActiveItem); } -.sidebar #full-list ul li a { +.sidebar .full-list ul li a { padding-left: 15px; display: block; width: 100%; height: 24px; } -.sidebar #full-list ul li ul { +.sidebar .full-list ul li ul { display: none; margin: 9px 20px; margin-right: 0; } -.sidebar #full-list ul li ul li { +.sidebar .full-list ul li ul li { margin-right: 0; height: 20px; color: var(--sidebarAccentMain); } -.sidebar #full-list ul li ul li a { +.sidebar .full-list ul li ul li a { border-left: 1px solid var(--sidebarInactiveItemMarker); padding: 0 10px; height: 20px; } -.sidebar #full-list ul li ul li.current-hash > a:before { +.sidebar .full-list ul li ul li.current-hash > a:before { content: none; } -.sidebar #full-list ul li ul li > a:hover { +.sidebar .full-list ul li ul li > a:hover { border-color: var(--sidebarLanguageAccentBar); } -.sidebar #full-list ul li ul li.current-hash > a { +.sidebar .full-list ul li ul li.current-hash > a { color: var(--sidebarActiveItem); border-color: var(--sidebarLanguageAccentBar); } -.sidebar #full-list ul li ul li.current-hash > a { +.sidebar .full-list ul li ul li.current-hash > a { color: var(--sidebarActiveItem); margin-left: 0; } @@ -446,7 +397,7 @@ cursor: pointer; background-color: transparent; border: none; - padding: 15px 11px; + padding: 18px 8px 18px 11px; font-size: var(--sidebarFontSize); } @@ -457,8 +408,10 @@ .sidebar-button { color: var(--sidebarAccentMain); } + .sidebar-closed .sidebar-button { color: var(--contrast); + margin: 12px 8px 8px 8px; } @media screen and (max-height: 500px) { @@ -466,7 +419,13 @@ overflow-y: auto; } - .sidebar #full-list { + .sidebar .full-list { overflow: visible; } } + +@media screen and (max-width: 768px) { + .sidebar-closed .sidebar-button { + margin: 8px; + } +} diff --git a/assets/js/autocomplete/autocomplete-list.js b/assets/js/autocomplete/autocomplete-list.js index 346aa6f70..3c73e61b0 100644 --- a/assets/js/autocomplete/autocomplete-list.js +++ b/assets/js/autocomplete/autocomplete-list.js @@ -2,6 +2,7 @@ import { getSuggestions } from './suggestions' import { isBlank, qs } from '../helpers' export const AUTOCOMPLETE_CONTAINER_SELECTOR = '.autocomplete' +export const AUTOCOMPLETE_SUGGESTION_LIST_SELECTOR = '.autocomplete-suggestions' export const AUTOCOMPLETE_SUGGESTION_SELECTOR = '.autocomplete-suggestion' const state = { @@ -88,6 +89,9 @@ export function moveAutocompleteSelection (offset) { if (elementToSelect) { elementToSelect.classList.add('selected') + elementToSelect.scrollIntoView({ block: 'nearest' }) + } else { + qs(AUTOCOMPLETE_SUGGESTION_LIST_SELECTOR).scrollTop = 0 } } diff --git a/assets/js/autocomplete/suggestions.js b/assets/js/autocomplete/suggestions.js index e8d412326..395d34c64 100644 --- a/assets/js/autocomplete/suggestions.js +++ b/assets/js/autocomplete/suggestions.js @@ -28,7 +28,7 @@ const SUGGESTION_CATEGORY = { * @param {Number} limit The maximum number of results to return. * @returns {Suggestion[]} List of suggestions sorted and limited. */ -export function getSuggestions (query, limit = 5) { +export function getSuggestions (query, limit = 8) { if (isBlank(query)) { return [] } @@ -149,13 +149,24 @@ function nodeSectionSuggestion (node, section, query, category) { */ function moduleChildNodeSuggestion (childNode, parentId, query, category, label) { // Match "Module.function" format. - const modFun = `${parentId}.${childNode.id}` - if (!matchesAll(modFun, query)) { return null } + const modFunElixir = `${parentId}.${childNode.id}` + const modFunErlang = `${parentId}:${childNode.id}` + let modFun + let modFunRe + if (matchesAll(modFunElixir, query)) { + modFun = modFunElixir + modFunRe = /\./g + } else if (matchesAll(modFunErlang, query)) { + modFun = modFunErlang + modFunRe = /:/g + } else { + return null + } // When match spans both module and function name (i.e. ">Map.fefe { @@ -81,3 +81,23 @@ function getLivebookDevRunUrl (notebookUrl) { function getLivebookImportUrl (livebookUrl, notebookUrl) { return `${livebookUrl}/import?url=${encodeURIComponent(notebookUrl)}` } + +// Check if the device width is below a certain threshold (e.g., 768px for mobile) +document.addEventListener('click', function (e) { + if (window.innerWidth <= 768) { + const target = e.target.closest('a[href^="#"]') + if (target) { + e.preventDefault() + const targetId = target.getAttribute('href').substring(1) + const targetElement = document.getElementById(targetId) + if (targetElement) { + const offset = 45 // Adjust this offset as needed + const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY - offset + window.scrollTo({ + top: targetPosition, + behavior: 'smooth' + }) + } + } + } +}) diff --git a/assets/js/copy-button.js b/assets/js/copy-button.js index c5cfcb7fc..b2ec9ebeb 100644 --- a/assets/js/copy-button.js +++ b/assets/js/copy-button.js @@ -1,6 +1,6 @@ import { qsAll } from './helpers' -const BUTTON = '' +const BUTTON = '' /** * Initializes copy buttons. @@ -22,6 +22,7 @@ function addCopyButtons () { Array.from(qsAll('.copy-button')).forEach(button => { let timeout button.addEventListener('click', () => { + const ariaLiveContent = button.querySelector('[aria-live]') timeout && clearTimeout(timeout) const text = @@ -32,7 +33,11 @@ function addCopyButtons () { navigator.clipboard.writeText(text) button.classList.add('clicked') - timeout = setTimeout(() => button.classList.remove('clicked'), 3000) + ariaLiveContent.innerHTML = 'Copied! ✓' + timeout = setTimeout(() => { + button.classList.remove('clicked') + ariaLiveContent.innerHTML = '' + }, 3000) }) }) } diff --git a/assets/js/entry/epub.js b/assets/js/entry/epub.js index ca2080c52..abf37e456 100644 --- a/assets/js/entry/epub.js +++ b/assets/js/entry/epub.js @@ -1,3 +1,8 @@ +import { onDocumentReady } from '../helpers' +import { fixBlockquotes } from '../content' import { initialize as initMakeup } from '../makeup' -initMakeup() +onDocumentReady(() => { + initMakeup() + fixBlockquotes() +}) diff --git a/assets/js/entry/html.js b/assets/js/entry/html.js index c5f05841e..04953e773 100644 --- a/assets/js/entry/html.js +++ b/assets/js/entry/html.js @@ -4,7 +4,7 @@ import { onDocumentReady } from '../helpers' import { initialize as initContent } from '../content' import { initialize as initSidebarDrawer } from '../sidebar/sidebar-drawer' import { initialize as initSidebarContent } from '../sidebar/sidebar-list' -import { initialize as initSidebarSearch } from '../sidebar/sidebar-search' +import { initialize as initSidebarSearch } from '../search-bar' import { initialize as initVersions } from '../sidebar/sidebar-version-select' import { initialize as initSearchPage } from '../search-page' import { initialize as initTheme } from '../theme' diff --git a/assets/js/handlebars/templates/autocomplete-suggestions.handlebars b/assets/js/handlebars/templates/autocomplete-suggestions.handlebars index eb3a9a268..69b9c03ae 100644 --- a/assets/js/handlebars/templates/autocomplete-suggestions.handlebars +++ b/assets/js/handlebars/templates/autocomplete-suggestions.handlebars @@ -1,8 +1,14 @@ +
- -
"{{term}}"
-
Search the documentation
-
+
+ Autocompletion results for + "{{term}}" . + + Press + RETURN + for full-text search. + +
{{#each suggestions}}
@@ -13,7 +19,7 @@ {{/if}} {{#if label}} - ({{label}}) + {{label}} {{/if}}
diff --git a/assets/js/handlebars/templates/modal-layout.handlebars b/assets/js/handlebars/templates/modal-layout.handlebars index 966ab405f..4ecb2d40f 100644 --- a/assets/js/handlebars/templates/modal-layout.handlebars +++ b/assets/js/handlebars/templates/modal-layout.handlebars @@ -2,7 +2,7 @@
- + <%# Extra content specified by the user (e.g. custom Javascript) %> <%= before_closing_body_tag(config, :html) %> diff --git a/lib/ex_doc/formatter/html/templates/head_template.eex b/lib/ex_doc/formatter/html/templates/head_template.eex index ffe4a1f28..730a4c589 100644 --- a/lib/ex_doc/formatter/html/templates/head_template.eex +++ b/lib/ex_doc/formatter/html/templates/head_template.eex @@ -9,6 +9,9 @@ <%= if config.authors do %> "> <% end %> + <%= if page.noindex do %> + + <% end %> <%= page.title %> — <%= config.project %> v<%= config.version %> " /> <%= if page.type == :cheatmd do %> diff --git a/lib/ex_doc/formatter/html/templates/module_template.eex b/lib/ex_doc/formatter/html/templates/module_template.eex index 195caef2b..a7d02bc0a 100644 --- a/lib/ex_doc/formatter/html/templates/module_template.eex +++ b/lib/ex_doc/formatter/html/templates/module_template.eex @@ -1,8 +1,7 @@ -<%= head_template(config, %{title: module.title, type: module.type}) %> +<%= head_template(config, %{title: module.title, type: module.type, noindex: false}) %> <%= sidebar_template(config, nodes_map) %>

- <%= settings_button_template() %> <%= if module.source_url do %> @@ -33,8 +32,8 @@

- Summary + Summary

<%= for {name, nodes} <- summary, do: summary_template(name, nodes) %> @@ -45,8 +44,8 @@

- <%= name %> + <%= name %>

<%= for node <- nodes, do: detail_template(node, module) %> diff --git a/lib/ex_doc/formatter/html/templates/not_found_template.eex b/lib/ex_doc/formatter/html/templates/not_found_template.eex index 718f5b5b4..ad9179760 100644 --- a/lib/ex_doc/formatter/html/templates/not_found_template.eex +++ b/lib/ex_doc/formatter/html/templates/not_found_template.eex @@ -1,8 +1,7 @@ -<%= head_template(config, %{title: "404", type: :extra}) %> +<%= head_template(config, %{title: "404", type: :extra, noindex: true}) %> <%= sidebar_template(config, nodes_map) %>

- <%= settings_button_template() %> Page not found

diff --git a/lib/ex_doc/formatter/html/templates/search_template.eex b/lib/ex_doc/formatter/html/templates/search_template.eex index 01b0f5545..54f553df0 100644 --- a/lib/ex_doc/formatter/html/templates/search_template.eex +++ b/lib/ex_doc/formatter/html/templates/search_template.eex @@ -1,9 +1,8 @@ -<%= head_template(config, %{title: "Search", type: :search}) %> +<%= head_template(config, %{title: "Search", type: :search, noindex: true}) %> <%= sidebar_template(config, nodes_map) %>