diff --git a/.gitattributes b/.gitattributes index a1ea7241..c2c9dae6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,5 @@ *.svg binary *.webp binary *.pdf binary +*.zip binary +*.woff2 binary diff --git a/.github/workflows/ui-check.yml b/.github/workflows/ui-check.yml new file mode 100644 index 00000000..779cf17e --- /dev/null +++ b/.github/workflows/ui-check.yml @@ -0,0 +1,30 @@ +name: UI check + +on: + pull_request: + paths: + - 'content/supplemental/**' + - 'tools/check-css.mjs' + - 'tools/check-css.test.mjs' + - '.github/workflows/ui-check.yml' + +permissions: + contents: read + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Test the stylesheet check + run: node --test tools/check-css.test.mjs + + - name: Check the stylesheets + run: node tools/check-css.mjs diff --git a/AGENTS.md b/AGENTS.md index 3afa90cc..a42c507e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,6 +94,22 @@ See `CONTRIBUTING.md` for the full table. Bypass (rarely) with `git commit --no- Screenshots are captured at **2x** and declared at half their pixel width (`image::foo.png[width="413"]` for an 826px-wide file), so they stay sharp on high-DPI displays. `tools/screenshot-2x.mjs` does this: it drives Playwright directly with `deviceScaleFactor: 2` and captures a single element, which the Playwright MCP tool cannot do — its resize action takes a width and a height only, so every capture it makes is 1x. Run it with `--url`, `--selector` and `--out`; it prints the resulting pixel size and the width to declare. See the comment at the top of the file for the rest of its options. +A diagram drawn with dark lines on a transparent background disappears in the dark theme. Give its image macro `role=light-background`, as in `image::transactions/transactions-1.png[,500,role=light-background]`, which puts a white plate behind it. Screenshots have their own background and need no role. + +## UI: styles, tokens and the pinned bundle + +The site uses the Antora default UI bundle pinned in `ui/ui-bundle.zip`. `ui/README.md` says which revision it is and how to update it. The styles are ours and live in `content/supplemental/css/`: + +- `tokens.css` holds the design tokens in three tiers: palette, semantic roles, components. The dark theme is the `:root[data-theme="dark"]` block at the end of the file, inside `@media screen` so print stays light. It sets the tier 2 colors and shadow, and the tier 3 colors that must differ in the dark theme (header logo, code, admonitions). No other stylesheet has a rule under `data-theme="dark"`. The only custom properties declared elsewhere are the `--adm-*` aliases in the Admonitions section of `site.css`. +- `site.css` replaces the bundle stylesheet. Part 1 is the upstream `src/css` of the bundle's revision; change it only to port upstream changes, and override it in part 2 otherwise. Part 2 holds the Jmix styles, one section per component. A section that needs forced colors rules keeps them in its own `@media (forced-colors: active)` block. +- `search.css`, `dropdown-menu.css` and `feedback-form.css` style the search, the two header menus (version and color theme) and the feedback form. +- `content/supplemental/js/` holds the scripts that add to the bundle's `site.js`: `a11y.js` (ARIA state, copy button names, the skip link and keyboard access to search results), `code-toolbox.js` (starts a code block below the copy toolbox when the toolbox would cover its first line), `dropdown-menu.js` (the version menu), `theme-menu.js` (the color theme menu) and `feedback-form.js`. +- The color theme is the `data-theme` attribute (`light` or `dark`) on ``, and `data-theme-preference` holds the reader's choice (`system`, `light` or `dark`). Only `light` and `dark` are saved, in `localStorage` under `jmix-docs-theme`; choosing System removes the key. An inline script at the start of `content/supplemental/partials/head-styles.hbs` sets both attributes and adds a `color-scheme` meta element before the stylesheets load, so the page never shows the wrong theme; keep it before the stylesheet links. `theme-menu.js` changes them later. The DocsBot chat widget that Google Tag Manager adds draws itself for a light page, so `site.css` keeps its host, `#docsbotai-root`, at `color-scheme: light`. + +Outside `tokens.css`, write colors as `var(--…)`. Keywords such as `transparent`, `currentColor` and `inherit`, `color-mix()` over keywords and tokens, and the CSS system colors in forced colors blocks are the only exceptions. `node tools/check-css.mjs` rejects other color literals and reports custom properties that are used but never declared. It also makes sure the dark block sets every `--color-*` and `--shadow-*` token of the light block, and only tokens that the light block declares. CI runs it, with its tests (`node --test tools/check-css.test.mjs`), on pull requests that change `content/supplemental/` or the check. + +After a UI change, build the site and run `node tools/ui-audit.mjs`. It checks keyboard focus, contrast and the expected styles in both themes, the skip link, accessible names, keyboard access to search results, fonts, forced colors mode, and the color theme: that it is set before the first stylesheet, and how the theme menu behaves. It needs Playwright with Chromium, which is not a dependency of this repository. Run `npx playwright@latest install chromium` once. The built pages load the production analytics container, so check them only through the audit or a Playwright script that blocks external hosts. Do not open them in a browser pane or capture them with `tools/screenshot-2x.mjs`, because neither blocks the container. + ## Conventions - In multi-locale examples use German (`de`) as the secondary locale. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1cb2c941..0a74db23 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -180,6 +180,14 @@ pngquant --quality=65-85 --strip --force --ext .png path/to/screenshot.png Typical reduction for UI screenshots: 70-80% size, no visible quality loss. +### Images in the dark theme + +Screenshots keep their own light background in the dark theme, but a diagram drawn with dark lines on a transparent background disappears. Add `role=light-background` to the image macro of such a diagram, for example `image::transactions/transactions-1.png[,500,role=light-background]`, or export it with a white background. Check new pages in both themes by choosing Light and Dark in the theme menu in the header. + +## Changing the UI + +UI styles live in `content/supplemental/css/`. Use the custom properties from `tokens.css` for colors; `node tools/check-css.mjs` rejects color literals in the other files. A new `--color-*` or `--shadow-*` token needs a value in the dark block of `tokens.css` too; the check reports it when it is missing. Build the site and run `node tools/ui-audit.mjs` before you open a pull request; it needs Playwright with Chromium (`npx playwright@latest install chromium`). The default UI bundle is pinned; see `ui/README.md` before you replace it. + ## Repository history This repository was created in May 2026 with a fresh history; previous history (including pre-cleanup image revisions) is preserved in the archived repo at [TBD: link to old repo]. Refer there for `git blame` and commit history older than the initial commit. diff --git a/antora-playbook.ci.yml b/antora-playbook.ci.yml index e786c73b..578641b8 100644 --- a/antora-playbook.ci.yml +++ b/antora-playbook.ci.yml @@ -78,8 +78,7 @@ content: start_path: doc ui: bundle: - url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/master/raw/build/ui-bundle.zip?job=bundle-stable - snapshot: true + url: ./ui/ui-bundle.zip supplemental_files: ./content/supplemental asciidoc: extensions: diff --git a/antora-playbook.yml b/antora-playbook.yml index b72ed285..5a4b83a4 100644 --- a/antora-playbook.yml +++ b/antora-playbook.yml @@ -60,8 +60,7 @@ content: start_path: doc ui: bundle: - url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/master/raw/build/ui-bundle.zip?job=bundle-stable - snapshot: true + url: ./ui/ui-bundle.zip supplemental_files: ./content/supplemental asciidoc: extensions: diff --git a/content/modules/bpm/pages/bpmn/bpmn-events.adoc b/content/modules/bpm/pages/bpmn/bpmn-events.adoc index 4669736f..e2b14e4b 100644 --- a/content/modules/bpm/pages/bpmn/bpmn-events.adoc +++ b/content/modules/bpm/pages/bpmn/bpmn-events.adoc @@ -350,7 +350,7 @@ image::bpmn-events/end-event-not-mandatory.png[,250] Don’t try to bring all flows to the single end event – it only makes your diagram messy. -image::bpmn-events/end-events-examples.png[,500] +image::bpmn-events/end-events-examples.png[,500,role=light-background] Multiple end events allow to analyze how processes ended. diff --git a/content/modules/bpm/pages/bpmn/bpmn-service-task.adoc b/content/modules/bpm/pages/bpmn/bpmn-service-task.adoc index 5cbaad67..5ad1e5b1 100644 --- a/content/modules/bpm/pages/bpmn/bpmn-service-task.adoc +++ b/content/modules/bpm/pages/bpmn/bpmn-service-task.adoc @@ -221,7 +221,7 @@ If more than one service tasks within a process refer to the same Java Delegate for each will be created a separate instance. All process instances share the corresponding class instance for the task. -image::bpmn-service-task/java-delegate-instantiating.png[,600] +image::bpmn-service-task/java-delegate-instantiating.png[,600,role=light-background] This means that the class must not use any member variables and must be thread-safe, as it can be executed simultaneously from different threads. This also may affect xref:field-injections[Fields injection]. diff --git a/content/modules/bpm/pages/bpmn/transactions.adoc b/content/modules/bpm/pages/bpmn/transactions.adoc index 9ecfa5db..80e69e7a 100644 --- a/content/modules/bpm/pages/bpmn/transactions.adoc +++ b/content/modules/bpm/pages/bpmn/transactions.adoc @@ -12,7 +12,7 @@ the engine performs a depth-first search through the process graph and will return once it has reached <> on every branch of execution. This is illustrated in the following picture: -image::transactions/transactions-1.png[,500] +image::transactions/transactions-1.png[,500,role=light-background] We see a segment of a BPMN process with a user task, a service task and a timer event. Completing the user task and validating the address is part of the same unit of work, @@ -52,7 +52,7 @@ in order to be able to scope logical units of work. This is where *asynchronous continuations* come into play. Consider the following process (fragment): -image::bpm:/transactions/transactions-2.png[,800] +image::bpm:/transactions/transactions-2.png[,800,role=light-background] In the next case, we are completing the user task, generating an invoice and then sending that invoice to the customer. This time the generation of the invoice is not part of the same unit of work, diff --git a/content/modules/bpm/pages/dmn-1-3.adoc b/content/modules/bpm/pages/dmn-1-3.adoc index 16e60919..e8891f30 100644 --- a/content/modules/bpm/pages/dmn-1-3.adoc +++ b/content/modules/bpm/pages/dmn-1-3.adoc @@ -95,7 +95,7 @@ image::dmn/add-input.png[,900] A business rule is one or more logical conditions based on input parameters, connected by the logical operator 'AND'. -image::dmn/business-rule-full.png[,900] +image::dmn/business-rule-full.png[,900,role=light-background] For example, color == "red" AND size > 10. diff --git a/content/modules/bpm/pages/process-artifacts.adoc b/content/modules/bpm/pages/process-artifacts.adoc index ac3a7840..11054706 100644 --- a/content/modules/bpm/pages/process-artifacts.adoc +++ b/content/modules/bpm/pages/process-artifacts.adoc @@ -23,7 +23,7 @@ At runtime, for specific branches (or paths) of the process, < a { - color: inherit; display: block; - padding: 0.5rem 0 0.5rem 1rem; margin-bottom: 0.25rem; + padding: 0.5rem 0 0.5rem 1rem; + border-radius: var(--radius-md); + color: inherit; } .search-result-document-hit > a:hover { - background-color: rgba(69, 142, 225, 0.05); -} - -.header-btn { - display: flex; - margin-right: 20px; - text-align: center; -} - -.header-link, .header-purple-link { - display: inline-block; - padding: 12px 30px; - margin-top: 11px; - margin-bottom: 11px; - font-weight: 600; - font-size: 14px; - border-radius: 6px; + background: var(--color-surface-hover); text-decoration: none; - outline: none; - cursor: pointer; -} - -.header-link { - color: #17124b; - background: #fdb42b; -} - -.header-purple-link { - padding: 10px 30px; - color: white; - background-color: #17124b; -} - -.header-link:hover, .header-purple-link:hover { - text-decoration: underline; -} - -.navbar-brand .navbar-item:first-child { - font-size: 1rem; } -@media screen and (max-width: 420px) { - .navbar-brand .navbar-item:first-child { - font-size: .75rem; +@media screen and (max-width: 1023.5px) { + /* the menu panel scrolls on small screens and would clip the results: let them hang over the page below it */ + .navbar-menu { + overflow: visible; } -} -@media screen and (max-width: 340px) { - .navbar-brand .vertical-line { - display: none; + /* the panel starts lower here, below the field in the open menu, so it gets less height */ + .search-result-dataset { + max-height: calc(100vh - 8rem); } } -a.jmix-link, a.slack-link, a.git-link, a.ai-assistant-link { - font-size: 0; - padding-left: 2px; - padding-right: 10px; -} - -:is(a.slack-link, a.jmix-link, a.git-link, a.ai-assistant-link) img { - margin-left: 0; -} - -:is(a.slack-link, a.jmix-link, a.git-link, a.ai-assistant-link):hover img { - transform: scale(1.07); -} - -.git-link, .slack-link, .jmix-link, .ai-assistant-link { - margin-right: 4px; -} - -div.header-btn a img { - height: 1.25rem; - vertical-align: text-bottom; - margin-left: 8px; - height: 1.25rem; - display: flex; -} - -a.ai-assistant-link { - margin-right: 4px; -} - -a.ai-assistant-link img { - content: url("../img/jmix-ai-assistant-icon.svg"); -} - -a.git-link img { - content: url("../img/git-icon.svg"); -} +@media (forced-colors: active) { + #search-input, + .search-result-dataset { + border-color: CanvasText; + } -a.slack-link img { - content: url("../img/slack-icon.svg"); + .search-field::before { + forced-color-adjust: none; + background-color: GrayText; + } } - -a.jmix-link img { - content: url("../img/jmix-icon.svg"); -} \ No newline at end of file diff --git a/content/supplemental/css/site.css b/content/supplemental/css/site.css new file mode 100644 index 00000000..5b969585 --- /dev/null +++ b/content/supplemental/css/site.css @@ -0,0 +1,4014 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL + * was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Stylesheet of the Jmix docs UI. It replaces css/site.css of the Antora default UI bundle (ui/ui-bundle.zip). + * + * Part 1 is src/css of antora-ui-default at revision 0e38223a, the revision of the bundle, concatenated in the + * order of its site.css; vars.css lives in tokens.css. Part 1 has three kinds of edits: the bundle typeface + * sections are replaced by the Typefaces section of part 2, the focus outline resets are removed, and color + * literals are replaced with tokens. ui/README.md describes how to port upstream changes. Part 2 holds the Jmix + * styles and overrides part 1 where the design differs. Colors come only from tokens.css + * (node tools/check-css.mjs). + */ + +/* ==== Part 1: Antora default UI ==== */ + +/* ---- upstream: typeface-roboto.css ---- */ + +/* replaced by part 2, "Typefaces" */ + +/* ---- upstream: typeface-roboto-mono.css ---- */ + +/* replaced by part 2, "Typefaces" */ + +/* ---- upstream: base.css ---- */ + +*, +*::before, +*::after { + box-sizing: inherit; +} + +html { + box-sizing: border-box; + font-size: var(--body-font-size); + height: 100%; + scroll-behavior: smooth; +} + +@media screen and (min-width: 1024px) { + html { + font-size: var(--body-font-size--desktop); + } +} + +body { + background: var(--body-background); + color: var(--body-font-color); + font-family: var(--body-font-family); + line-height: var(--body-line-height); + margin: 0; + tab-size: 4; + word-wrap: anywhere; /* aka overflow-wrap; used when hyphens are disabled or aren't sufficient */ +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +a:active { + background-color: none; +} + +code, +kbd, +pre { + font-family: var(--monospace-font-family); +} + +b, +dt, +strong, +th { + font-weight: var(--body-font-weight-bold); +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +em em { /* stylelint-disable-line */ + font-style: normal; +} + +strong strong { /* stylelint-disable-line */ + font-weight: normal; +} + +button { + cursor: pointer; + font-family: inherit; + font-size: 1em; + line-height: var(--body-line-height); + margin: 0; +} + +button::-moz-focus-inner { + border: none; + padding: 0; +} + +summary { + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +table { + border-collapse: collapse; + word-wrap: normal; /* table widths aren't computed as expected when word-wrap is enabled */ +} + +object[type="image/svg+xml"]:not([width]) { + width: fit-content; +} + +.hide, +.is-hidden { + display: none !important; +} + +::placeholder { + opacity: 0.5; +} + +@media (pointer: fine) { + @supports (scrollbar-width: thin) { + html { + scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color); + } + + body * { + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-thumb-color) transparent; + } + } + + html::-webkit-scrollbar { + background-color: var(--scrollbar-track-color); + height: 12px; + width: 12px; + } + + body ::-webkit-scrollbar { + height: 6px; + width: 6px; + } + + ::-webkit-scrollbar-thumb { + background-clip: padding-box; + background-color: var(--scrollbar-thumb-color); + border: 3px solid transparent; + border-radius: 12px; + } + + body ::-webkit-scrollbar-thumb { + border-width: 1.75px; + border-radius: 6px; + } + + ::-webkit-scrollbar-thumb:hover { + background-color: var(--scrollbar_hover-thumb-color); + } +} + +/* ---- upstream: body.css ---- */ + +@media screen and (min-width: 1024px) { + .body { + display: flex; + } +} + +/* ---- upstream: nav.css ---- */ + +@media screen and (max-width: 1023.5px) { + html.is-clipped--nav { + overflow-y: hidden; + } +} + +.nav-container { + position: fixed; + top: var(--navbar-height); + left: 0; + width: 100%; + font-size: calc(17 / var(--rem-base) * 1rem); + z-index: var(--z-index-nav); + visibility: hidden; +} + +@media screen and (min-width: 769px) { + .nav-container { + width: var(--nav-width); + } +} + +@media screen and (min-width: 1024px) { + .nav-container { + font-size: calc(15.5 / var(--rem-base) * 1rem); + flex: none; + position: static; + top: 0; + visibility: visible; + } +} + +.nav-container.is-active { + visibility: visible; +} + +.nav { + background: var(--nav-background); + position: relative; + top: var(--toolbar-height); + height: var(--nav-height); +} + +@media screen and (min-width: 769px) { + .nav { + box-shadow: 0.5px 0 3px var(--nav-border-color); + } +} + +@media screen and (min-width: 1024px) { + .nav { + top: var(--navbar-height); + box-shadow: none; + position: sticky; + height: var(--nav-height--desktop); + } +} + +.nav a { + color: inherit; +} + +.nav .panels { + display: flex; + flex-direction: column; + height: inherit; +} + +.nav-panel-menu { + overflow-y: scroll; + overscroll-behavior: none; + height: var(--nav-panel-menu-height); +} + +.nav-panel-menu:only-child { + height: 100%; +} + +.nav-panel-menu:not(.is-active) .nav-menu { + opacity: 0.75; +} + +.nav-panel-menu:not(.is-active)::after { + content: ""; + background: var(--color-overlay); + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.nav-menu { + min-height: 100%; + padding: 0.5rem 0.75rem; + line-height: var(--nav-line-height); + position: relative; +} + +.nav-menu-toggle { + background: transparent url(../img/octicons-16.svg#view-unfold) no-repeat center / 100% 100%; + border: none; + float: right; + height: 1em; + margin-right: -0.5rem; + opacity: 0.75; + padding: 0; + position: sticky; + top: calc((var(--nav-line-height) - 1 + 0.5) * 1rem); + visibility: hidden; + width: 1em; +} + +.nav-menu-toggle.is-active { + background-image: url(../img/octicons-16.svg#view-fold); +} + +.nav-panel-menu.is-active:hover .nav-menu-toggle { + visibility: visible; +} + +.nav-menu h3.title { + color: var(--nav-heading-font-color); + font-size: inherit; + font-weight: var(--body-font-weight-bold); + margin: 0; + padding: 0.25em 0 0.125em; +} + +.nav-list { + list-style: none; + margin: 0 0 0 0.75rem; + padding: 0; +} + +.nav-menu > .nav-list + .nav-list { + margin-top: 0.5rem; +} + +.nav-item { + margin-top: 0.5em; +} + +/* adds some breathing room below a nested list */ +.nav-item-toggle ~ .nav-list { + padding-bottom: 0.125rem; +} + +/* matches list without a title */ +.nav-item[data-depth="0"] > .nav-list:first-child { + display: block; + margin: 0; +} + +.nav-item:not(.is-active) > .nav-list { + display: none; +} + +.nav-item-toggle { + background: transparent url(../img/caret.svg) no-repeat center / 50%; + border: none; + line-height: inherit; + padding: 0; + position: absolute; + height: calc(var(--nav-line-height) * 1em); + width: calc(var(--nav-line-height) * 1em); + margin-top: -0.05em; + margin-left: calc(var(--nav-line-height) * -1em); +} + +.nav-item.is-active > .nav-item-toggle { + transform: rotate(90deg); +} + +.is-current-page > .nav-link, +.is-current-page > .nav-text { + font-weight: var(--body-font-weight-bold); +} + +.nav-panel-explore { + background: var(--nav-background); + display: flex; + flex-direction: column; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.nav-panel-explore:not(:first-child) { + top: auto; + max-height: var(--nav-panel-explore-height); +} + +.nav-panel-explore .context { + font-size: calc(15 / var(--rem-base) * 1rem); + flex-shrink: 0; + color: var(--nav-muted-color); + box-shadow: 0 -1px 0 var(--nav-panel-divider-color); + padding: 0 0.5rem; + display: flex; + align-items: center; + justify-content: space-between; + line-height: 1; + height: var(--drawer-height); +} + +.nav-panel-explore:not(:first-child) .context { + cursor: pointer; +} + +.nav-panel-explore .context .version { + display: flex; + align-items: inherit; +} + +.nav-panel-explore .context .version::after { + content: ""; + background: url(../img/chevron.svg) no-repeat center right / auto 100%; + width: 1.25em; + height: 0.75em; +} + +.nav-panel-explore .components { + line-height: var(--nav-line-height); + flex-grow: 1; + box-shadow: inset 0 1px 5px var(--nav-panel-divider-color); + background: var(--nav-secondary-background); + padding: 0.75rem 0.75rem 0 0.75rem; + margin: 0; + overflow-y: scroll; + overscroll-behavior: none; + max-height: 100%; + display: block; +} + +.nav-panel-explore:not(.is-active) .components { + display: none; +} + +.nav-panel-explore .component { + display: block; +} + +.nav-panel-explore .component + .component { + margin-top: 0.75rem; +} + +.nav-panel-explore .component:last-child { + margin-bottom: 0.75rem; +} + +.nav-panel-explore .component .title { + font-weight: var(--body-font-weight-bold); + text-indent: 0.375rem hanging; +} + +.nav-panel-explore .versions { + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin: -0.125rem -0.375rem 0 0.375rem; + line-height: 1; + list-style: none; +} + +.nav-panel-explore .component .version { + margin: 0.375rem 0.375rem 0 0; +} + +.nav-panel-explore .component .version a { + background: var(--nav-border-color); + border-radius: 0.25rem; + white-space: nowrap; + padding: 0.25em 0.5em; + display: inherit; + opacity: 0.75; +} + +.nav-panel-explore .component .is-current a { + background: var(--nav-heading-font-color); + color: var(--nav-secondary-background); + font-weight: var(--body-font-weight-bold); + opacity: 1; +} + +/* ---- upstream: main.css ---- */ + +body.-toc aside.toc.sidebar { + display: none; +} + +@media screen and (max-width: 1023.5px) { + aside.toc.sidebar { + display: none; + } + + main > .content { + overflow-x: auto; + } +} + +@media screen and (min-width: 1024px) { + main { + flex: auto; + min-width: 0; /* min-width: 0 required for flexbox to constrain overflowing elements */ + } + + main > .content { + display: flex; + } + + aside.toc.embedded { + display: none; + } + + aside.toc.sidebar { + flex: 0 0 var(--toc-width); + order: 1; + } +} + +@media screen and (min-width: 1216px) { + aside.toc.sidebar { + flex-basis: var(--toc-width--widescreen); + } +} + +/* ---- upstream: toolbar.css ---- */ + +.toolbar { + color: var(--toolbar-font-color); + align-items: center; + background-color: var(--toolbar-background); + box-shadow: 0 1px 0 var(--toolbar-border-color); + display: flex; + font-size: calc(15 / var(--rem-base) * 1rem); + height: var(--toolbar-height); + justify-content: flex-start; + position: sticky; + top: var(--navbar-height); + z-index: var(--z-index-toolbar); +} + +.toolbar a { + color: inherit; +} + +.nav-toggle { + background: url(../img/menu.svg) no-repeat 50% 47.5%; + background-size: 49%; + border: none; + line-height: inherit; + padding: 0; + height: var(--toolbar-height); + width: var(--toolbar-height); + margin-right: -0.25rem; +} + +@media screen and (min-width: 1024px) { + .nav-toggle { + display: none; + } +} + +.nav-toggle.is-active { + background-image: url(../img/back.svg); + background-size: 41.5%; +} + +.home-link { + display: block; + background: url(../img/home-o.svg) no-repeat center; + height: calc(var(--toolbar-height) / 2); + width: calc(var(--toolbar-height) / 2); + margin: calc(var(--toolbar-height) / 4); +} + +.home-link:hover, +.home-link.is-current { + background-image: url(../img/home.svg); +} + +.edit-this-page { + display: none; + padding-right: 0.5rem; +} + +@media screen and (min-width: 1024px) { + .edit-this-page { + display: block; + } +} + +.toolbar .edit-this-page a { + color: var(--toolbar-muted-color); +} + +/* ---- upstream: breadcrumbs.css ---- */ + +.breadcrumbs { + display: none; + flex: 1 1; + padding: 0 0.5rem 0 0.75rem; + line-height: 1.25; +} + +@media screen and (min-width: 1024px) { + .breadcrumbs { + display: block; + } +} + +a + .breadcrumbs { + padding-left: 0.05rem; +} + +.breadcrumbs ul { + display: flex; + flex-wrap: wrap; + margin: 0; + padding: 0; + list-style: none; +} + +.breadcrumbs li { + display: inline; + margin: 0; +} + +.breadcrumbs li::after { + content: "/"; + padding: 0 0.5rem; +} + +.breadcrumbs li:last-of-type::after { + content: none; +} + +/* ---- upstream: page-versions.css ---- */ + +.page-versions { + margin: 0 0.2rem 0 auto; + position: relative; + line-height: 1; +} + +@media screen and (min-width: 1024px) { + .page-versions { + margin-right: 0.7rem; + } +} + +.page-versions .version-menu-toggle { + color: inherit; + background: url(../img/chevron.svg) no-repeat; + background-position: right 0.5rem top 50%; + background-size: auto 0.75em; + border: none; + line-height: inherit; + padding: 0.5rem 1.5rem 0.5rem 0.5rem; + position: relative; + z-index: var(--z-index-page-version-menu); +} + +.page-versions .version-menu { + display: flex; + min-width: 100%; + flex-direction: column; + align-items: flex-end; + background: linear-gradient(to bottom, var(--page-version-menu-background) 0%, var(--page-version-menu-background) 100%) no-repeat; + padding: 1.375rem 1.5rem 0.5rem 0.5rem; + position: absolute; + top: 0; + right: 0; + white-space: nowrap; +} + +.page-versions:not(.is-active) .version-menu { + display: none; +} + +.page-versions .version { + display: block; + padding-top: 0.5rem; +} + +.page-versions .version.is-current { + display: none; +} + +.page-versions .version.is-missing { + color: var(--page-version-missing-font-color); + font-style: italic; + text-decoration: none; +} + +/* ---- upstream: toc.css ---- */ + +.toc-menu { + color: var(--toc-font-color); +} + +.toc.sidebar .toc-menu { + margin-right: 0.75rem; + position: sticky; + top: var(--toc-top); +} + +.toc .toc-menu h3 { + color: var(--toc-heading-font-color); + font-size: calc(16 / var(--rem-base) * 1rem); + font-weight: var(--body-font-weight-bold); + line-height: 1.3; + margin: 0 -0.5px; + padding-bottom: 0.25rem; +} + +.toc.sidebar .toc-menu h3 { + display: flex; + flex-direction: column; + height: 2.5rem; + justify-content: flex-end; +} + +.toc .toc-menu ul { + font-size: calc(15 / var(--rem-base) * 1rem); + line-height: var(--toc-line-height); + list-style: none; + margin: 0; + padding: 0; +} + +.toc.sidebar .toc-menu ul { + max-height: var(--toc-height); + overflow-y: auto; + overscroll-behavior: none; +} + +@supports (scrollbar-width: none) { + .toc.sidebar .toc-menu ul { + scrollbar-width: none; + } +} + +.toc .toc-menu ul::-webkit-scrollbar { + width: 0; + height: 0; +} + +@media screen and (min-width: 1024px) { + .toc .toc-menu h3 { + font-size: calc(15 / var(--rem-base) * 1rem); + } + + .toc .toc-menu ul { + font-size: calc(13.5 / var(--rem-base) * 1rem); + } +} + +.toc .toc-menu li { + margin: 0; +} + +.toc .toc-menu li[data-level="2"] a { + padding-left: 1.25rem; +} + +.toc .toc-menu li[data-level="3"] a { + padding-left: 2rem; +} + +.toc .toc-menu a { + color: inherit; + border-left: 2px solid var(--toc-border-color); + display: inline-block; + padding: 0.25rem 0 0.25rem 0.5rem; + text-decoration: none; +} + +.sidebar.toc .toc-menu a { + display: block; +} + +.toc .toc-menu a:hover { + color: var(--link-font-color); +} + +.toc .toc-menu a.is-active { + border-left-color: var(--link-font-color); + color: var(--doc-font-color); +} + +.sidebar.toc .toc-menu a:focus { + background: var(--panel-background); +} + +/* ---- upstream: doc.css ---- */ + +.doc { + color: var(--doc-font-color); + font-size: var(--doc-font-size); + hyphens: auto; + line-height: var(--doc-line-height); + margin: var(--doc-margin); + max-width: var(--doc-max-width); + padding: 0 1rem 4rem; +} + +@media screen and (min-width: 1024px) { + .doc { + flex: auto; + font-size: var(--doc-font-size--desktop); + margin: var(--doc-margin--desktop); + max-width: var(--doc-max-width--desktop); + min-width: 0; + } +} + +.doc h1, +.doc h2, +.doc h3, +.doc h4, +.doc h5, +.doc h6 { + color: var(--heading-font-color); + font-weight: var(--heading-font-weight); + hyphens: none; + line-height: 1.3; + margin: 1rem 0 0; +} + +.doc > h1.page:first-child { + font-size: calc(36 / var(--rem-base) * 1rem); + margin: 1.5rem 0; +} + +@media screen and (min-width: 769px) { + .doc > h1.page:first-child { + margin-top: 2.5rem; + } +} + +.doc > h1.page:first-child + aside.toc.embedded { + margin-top: -0.5rem; +} + +.doc > h2#name + .sectionbody { + margin-top: 1rem; +} + +#preamble + .sect1, +.doc .sect1 + .sect1 { + margin-top: 2rem; +} + +.doc h1.sect0 { + background: var(--abstract-background); + font-size: 1.8em; + margin: 1.5rem -1rem 0; + padding: 0.5rem 1rem; +} + +.doc h2:not(.discrete) { + border-bottom: 1px solid var(--section-divider-color); + margin-left: -1rem; + margin-right: -1rem; + padding: 0.4rem 1rem 0.1rem; +} + +.doc h3:not(.discrete), +.doc h4:not(.discrete) { + font-weight: var(--alt-heading-font-weight); +} + +.doc h1 .anchor, +.doc h2 .anchor, +.doc h3 .anchor, +.doc h4 .anchor, +.doc h5 .anchor, +.doc h6 .anchor { + position: absolute; + text-decoration: none; + width: 1.75ex; + margin-left: -1.5ex; + visibility: hidden; + font-size: 0.8em; + font-weight: normal; + padding-top: 0.05em; +} + +.doc h1 .anchor::before, +.doc h2 .anchor::before, +.doc h3 .anchor::before, +.doc h4 .anchor::before, +.doc h5 .anchor::before, +.doc h6 .anchor::before { + content: "\00a7"; +} + +.doc h1:hover .anchor, +.doc h2:hover .anchor, +.doc h3:hover .anchor, +.doc h4:hover .anchor, +.doc h5:hover .anchor, +.doc h6:hover .anchor { + visibility: visible; +} + +.doc p, +.doc dl { + margin: 0; +} + +.doc a { + color: var(--link-font-color); +} + +.doc a:hover { + color: var(--link_hover-font-color); +} + +.doc a.bare { + hyphens: none; +} + +.doc a.unresolved { + color: var(--link_unresolved-font-color); +} + +.doc i.fa { + hyphens: none; + font-style: normal; +} + +.doc p code, +.doc thead code, +.doc .colist > table code { + color: var(--code-font-color); + background: var(--code-background); + border-radius: 0.25em; + font-size: 0.95em; + padding: 0.125em 0.25em; +} + +.doc code, +.doc pre { + hyphens: none; +} + +.doc pre { + font-size: calc(16 / var(--rem-base) * 1rem); + line-height: 1.5; + margin: 0; +} + +.doc blockquote { + margin: 0; +} + +.doc .paragraph.lead > p { + font-size: calc(19.5 / var(--rem-base) * 1rem); +} + +.doc .right { + float: right; +} + +.doc .left { + float: left; +} + +.doc .float-gap.right { + margin: 0 1rem 1rem 0; +} + +.doc .float-gap.left { + margin: 0 0 1rem 1rem; +} + +.doc .float-group::after { + content: ""; + display: table; + clear: both; +} + +.doc .text-left { + text-align: left; +} + +.doc .text-center { + text-align: center; +} + +.doc .text-right { + text-align: right; +} + +.doc .text-justify { + text-align: justify; +} + +.doc .stretch { + width: 100%; +} + +.doc .big { + font-size: larger; +} + +.doc .small { + font-size: smaller; +} + +.doc .underline { + text-decoration: underline; +} + +.doc .line-through { + text-decoration: line-through; +} + +.doc .paragraph, +.doc .dlist, +.doc .hdlist, +.doc .olist, +.doc .ulist, +.doc .exampleblock, +.doc .imageblock, +.doc .listingblock, +.doc .literalblock, +.doc .tabs, +.doc .sidebarblock, +.doc .verseblock, +.doc .videoblock, +.doc .quoteblock, +.doc .partintro, +.doc details, +.doc hr { + margin: 1rem 0 0; +} + +.doc > table.tableblock, +.doc > table.tableblock + *, +.doc .tablecontainer, +.doc .tablecontainer + *, +.doc :not(.tablecontainer) > table.tableblock, +.doc :not(.tablecontainer) > table.tableblock + * { + margin-top: 1.5rem; +} + +.doc table.tableblock { + font-size: calc(15 / var(--rem-base) * 1rem); +} + +.doc p.tableblock + p.tableblock { + margin-top: 0.5rem; +} + +.doc table.tableblock pre { + font-size: inherit; +} + +.doc td.tableblock > .content { + word-wrap: anywhere; /* aka overflow-wrap; used when hyphens are disabled or aren't sufficient */ +} + +.doc td.tableblock > .content > :first-child { + margin-top: 0; +} + +.doc table.tableblock th, +.doc table.tableblock td { + padding: 0.5rem; +} + +.doc table.tableblock, +.doc table.tableblock > * > tr > * { + border: 0 solid var(--table-border-color); +} + +.doc table.grid-all > * > tr > * { + border-width: 1px; +} + +.doc table.grid-cols > * > tr > * { + border-width: 0 1px; +} + +.doc table.grid-rows > * > tr > * { + border-width: 1px 0; +} + +.doc table.grid-all > thead th, +.doc table.grid-rows > thead th { + border-bottom-width: 2.5px; +} + +.doc table.frame-all { + border-width: 1px; +} + +.doc table.frame-ends { + border-width: 1px 0; +} + +.doc table.frame-sides { + border-width: 0 1px; +} + +.doc table.frame-none > colgroup + * > :first-child > *, +.doc table.frame-sides > colgroup + * > :first-child > * { + border-top-width: 0; +} + +/* NOTE let the grid win in case of frame-none */ +.doc table.frame-sides > :last-child > :last-child > * { + border-bottom-width: 0; +} + +.doc table.frame-none > * > tr > :first-child, +.doc table.frame-ends > * > tr > :first-child { + border-left-width: 0; +} + +.doc table.frame-none > * > tr > :last-child, +.doc table.frame-ends > * > tr > :last-child { + border-right-width: 0; +} + +.doc table.stripes-all > tbody > tr, +.doc table.stripes-odd > tbody > tr:nth-of-type(odd), +.doc table.stripes-even > tbody > tr:nth-of-type(even), +.doc table.stripes-hover > tbody > tr:hover { + background: var(--table-stripe-background); +} + +.doc table.tableblock > tfoot { + background: var(--table-footer-background); +} + +.doc .halign-left { + text-align: left; +} + +.doc .halign-right { + text-align: right; +} + +.doc .halign-center { + text-align: center; +} + +.doc .valign-top { + vertical-align: top; +} + +.doc .valign-bottom { + vertical-align: bottom; +} + +.doc .valign-middle { + vertical-align: middle; +} + +.doc .admonitionblock { + margin: 1.4rem 0 0; +} + +.doc .admonitionblock p, +.doc .admonitionblock td.content { + font-size: calc(16 / var(--rem-base) * 1rem); +} + +.doc .admonitionblock td.content > :not(.title):first-child, +.doc .admonitionblock td.content > .title + * { + margin-top: 0; +} + +.doc .admonitionblock td.content pre { + font-size: calc(15 / var(--rem-base) * 1rem); +} + +.doc .admonitionblock > table { + table-layout: fixed; + position: relative; + width: 100%; +} + +.doc .admonitionblock td.content { + padding: 1rem 1rem 0.75rem; + background: var(--admonition-background); + width: 100%; + word-wrap: anywhere; +} + +.doc .admonitionblock td.icon { + font-size: calc(15 / var(--rem-base) * 1rem); + left: 0; + line-height: 1; + padding: 0; + position: absolute; + top: 0; + transform: translate(-0.5rem, -50%); +} + +.doc .admonitionblock td.icon i { + align-items: center; + border-radius: 0.45rem; + display: inline-flex; + filter: initial; + height: 1.25rem; + padding: 0 0.5rem; + vertical-align: initial; + width: fit-content; +} + +.doc .admonitionblock td.icon i::after { + content: attr(title); + font-weight: var(--admonition-label-font-weight); + font-style: normal; + text-transform: uppercase; +} + +.doc .admonitionblock td.icon i.icon-caution { + background-color: var(--caution-color); + color: var(--caution-on-color); +} + +.doc .admonitionblock td.icon i.icon-important { + background-color: var(--important-color); + color: var(--important-on-color); +} + +.doc .admonitionblock td.icon i.icon-note { + background-color: var(--note-color); + color: var(--note-on-color); +} + +.doc .admonitionblock td.icon i.icon-tip { + background-color: var(--tip-color); + color: var(--tip-on-color); +} + +.doc .admonitionblock td.icon i.icon-warning { + background-color: var(--warning-color); + color: var(--warning-on-color); +} + +.doc .imageblock, +.doc .videoblock { + display: flex; + flex-direction: column; + align-items: center; +} + +.doc .imageblock .content { + align-self: stretch; + text-align: center; +} + +.doc .imageblock.text-left, +.doc .videoblock.text-left { + align-items: flex-start; +} + +.doc .imageblock.text-left .content { + text-align: left; +} + +.doc .imageblock.text-right, +.doc .videoblock.text-right { + align-items: flex-end; +} + +.doc .imageblock.text-right .content { + text-align: right; +} + +.doc .imageblock img, +.doc .imageblock object, +.doc .imageblock svg, +.doc .image > img, +.doc .image > object, +.doc .image > svg { + display: inline-block; + height: auto; + max-width: 100%; + vertical-align: middle; +} + +.doc .image:not(.left):not(.right) > img { + margin-top: -0.2em; +} + +.doc .videoblock iframe, +.doc .videoblock video { + max-width: 100%; + vertical-align: middle; +} + +#preamble .abstract blockquote { + background: var(--abstract-background); + border-left: 5px solid var(--abstract-border-color); + color: var(--abstract-font-color); + font-size: calc(16 / var(--rem-base) * 1rem); + padding: 0.75em 1em; +} + +.doc .quoteblock, +.doc .verseblock { + background: var(--quote-background); + border-left: 5px solid var(--quote-border-color); + color: var(--quote-font-color); +} + +.doc .quoteblock { + padding: 0.25rem 2rem 1.25rem; +} + +.doc .quoteblock .attribution { + color: var(--quote-attribution-font-color); + font-size: calc(15 / var(--rem-base) * 1rem); + margin-top: 0.75rem; +} + +.doc .quoteblock blockquote { + margin-top: 1rem; +} + +.doc .quoteblock .paragraph { + font-style: italic; +} + +.doc .quoteblock cite { + padding-left: 1em; +} + +.doc .verseblock { + font-size: 1.15em; + padding: 1rem 2rem; +} + +.doc .verseblock pre { + font-family: inherit; + font-size: inherit; +} + +.doc ol, +.doc ul { + margin: 0; + padding: 0 0 0 2rem; +} + +.doc ul.checklist, +.doc ul.none, +.doc ol.none, +.doc ul.no-bullet, +.doc ol.unnumbered, +.doc ul.unstyled, +.doc ol.unstyled { + list-style-type: none; +} + +.doc ul.no-bullet, +.doc ol.unnumbered { + padding-left: 1.25rem; +} + +.doc ul.unstyled, +.doc ol.unstyled { + padding-left: 0; +} + +.doc ul.circle { + list-style-type: circle; +} + +.doc ul.disc { + list-style-type: disc; +} + +.doc ul.square { + list-style-type: square; +} + +.doc ul.circle ul:not([class]), +.doc ul.disc ul:not([class]), +.doc ul.square ul:not([class]) { + list-style: inherit; +} + +.doc ol.arabic { + list-style-type: decimal; +} + +.doc ol.decimal { + list-style-type: decimal-leading-zero; +} + +.doc ol.loweralpha { + list-style-type: lower-alpha; +} + +.doc ol.upperalpha { + list-style-type: upper-alpha; +} + +.doc ol.lowerroman { + list-style-type: lower-roman; +} + +.doc ol.upperroman { + list-style-type: upper-roman; +} + +.doc ol.lowergreek { + list-style-type: lower-greek; +} + +.doc ul.checklist { + padding-left: 1rem; +} + +.doc ul.checklist p > i.fa-check-square-o:first-child, +.doc ul.checklist p > i.fa-square-o:first-child { + display: inline-flex; + justify-content: center; + width: 1em; +} + +.doc ul.checklist i.fa-check-square-o::before { + content: "\2713"; +} + +.doc ul.checklist i.fa-square-o::before { + content: "\274f"; +} + +.doc .dlist .dlist, +.doc .dlist .olist, +.doc .dlist .ulist, +.doc .olist .dlist, +.doc .olist .olist, +.doc .olist .ulist, +.doc .ulist .dlist, +.doc .ulist .olist, +.doc .ulist .ulist { + margin-top: 0.5rem; +} + +.doc .olist li + li, +.doc .ulist li + li { + margin-top: 0.5rem; +} + +.doc .ulist .listingblock, +.doc .olist .listingblock, +.doc .admonitionblock .listingblock { + padding: 0; +} + +.doc .admonitionblock .title, +.doc .exampleblock .title, +.doc .imageblock .title, +.doc .literalblock .title, +.doc .listingblock .title, +.doc .openblock .title, +.doc .videoblock .title, +.doc table.tableblock caption { + color: var(--caption-font-color); + font-size: calc(16 / var(--rem-base) * 1rem); + font-style: var(--caption-font-style); + font-weight: var(--caption-font-weight); + hyphens: none; + letter-spacing: 0.01em; + padding-bottom: 0.075rem; +} + +.doc table.tableblock caption { + text-align: left; +} + +.doc .olist .title, +.doc .ulist .title { + font-style: var(--caption-font-style); + font-weight: var(--caption-font-weight); + margin-bottom: 0.25rem; +} + +.doc .imageblock .title, +.doc .videoblock .title { + margin-top: 0.5rem; + padding-bottom: 0; +} + +.doc details { + margin-left: 1rem; +} + +.doc details > summary { + display: block; + position: relative; + line-height: var(--doc-line-height); + margin-bottom: 0.5rem; +} + +.doc details > summary::-webkit-details-marker { + display: none; +} + +.doc details > summary::before { + content: ""; + border: solid transparent; + border-left-color: currentColor; + border-width: 0.3em 0 0.3em 0.5em; + position: absolute; + top: calc((var(--doc-line-height) * 0.5 - 0.3) * 1em); + left: -1rem; + transform: translateX(15%); +} + +.doc details[open] > summary::before { + border-color: currentColor transparent transparent; + border-width: 0.5rem 0.3rem 0; + transform: translateY(15%); +} + +.doc details > summary::after { + content: ""; + width: 1rem; + height: 1em; + position: absolute; + top: calc((var(--doc-line-height) * 0.5 - 0.5) * 1em); + left: -1rem; +} + +.doc details.result { + margin-top: 0.25rem; +} + +.doc details.result > summary { + color: var(--caption-font-color); + font-style: italic; + margin-bottom: 0; +} + +.doc details.result > .content { + margin-left: -1rem; +} + +.doc .exampleblock > .content, +.doc details.result > .content { + background: var(--example-background); + border: 0.25rem solid var(--example-border-color); + border-radius: 0.5rem; + padding: 0.75rem; +} + +.doc .exampleblock > .content::after, +.doc details.result > .content::after { + content: ""; + display: table; + clear: both; +} + +.doc .exampleblock > .content > :first-child, +.doc details > .content > :first-child { + margin-top: 0; +} + +.doc .sidebarblock { + background: var(--sidebar-background); + border-radius: 0.75rem; + padding: 0.75rem 1.5rem; +} + +.doc .sidebarblock > .content > .title { + font-size: calc(22.5 / var(--rem-base) * 1rem); + font-weight: var(--alt-heading-font-weight); + line-height: 1.3; + margin-bottom: 0.5rem; + text-align: center; +} + +.doc .sidebarblock > .content > .title + *, +.doc .sidebarblock > .content > :not(.title):first-child { + margin-top: 0; +} + +/* NEEDS REVIEW prevent pre in table from causing article to exceed bounds */ +.doc table.tableblock pre, +.doc .listingblock.wrap pre { + white-space: pre-wrap; +} + +.doc pre.highlight > code, +.doc .listingblock pre:not(.highlight), +.doc .literalblock pre { + background: var(--pre-background); + box-shadow: inset 0 0 1.75px var(--pre-border-color); + display: block; + overflow-x: auto; + padding: 0.875em; +} + +.doc .listingblock > .content { + position: relative; +} + +.doc .source-toolbox { + display: flex; + visibility: hidden; + position: absolute; + top: 0.25rem; + right: 0.5rem; + color: var(--pre-annotation-font-color); + font-family: var(--body-font-family); + font-size: calc(13 / var(--rem-base) * 1rem); + line-height: 1; + user-select: none; + white-space: nowrap; + z-index: 1; +} + +.doc .listingblock:hover .source-toolbox { + visibility: visible; +} + +.doc .source-toolbox .source-lang { + text-transform: uppercase; + letter-spacing: 0.075em; +} + +.doc .source-toolbox > :not(:last-child)::after { + content: "|"; + letter-spacing: 0; + padding: 0 1ch; +} + +.doc .source-toolbox .copy-button { + display: flex; + flex-direction: column; + align-items: center; + background: none; + border: none; + color: inherit; + padding: 0; + font-size: inherit; + line-height: inherit; + width: 1em; + height: 1em; +} + +.doc .source-toolbox .copy-icon { + flex: none; + width: inherit; + height: inherit; +} + +.doc .source-toolbox img.copy-icon { + filter: invert(50.2%); +} + +.doc .source-toolbox svg.copy-icon { + fill: currentColor; +} + +.doc .source-toolbox .copy-toast { + flex: none; + position: relative; + display: inline-flex; + justify-content: center; + margin-top: 1em; + background-color: var(--doc-font-color); + border-radius: 0.25em; + padding: 0.5em; + color: var(--color-text-on-accent); + cursor: auto; + opacity: 0; + transition: opacity 0.5s ease 0.5s; +} + +.doc .source-toolbox .copy-toast::after { + content: ""; + position: absolute; + top: 0; + width: 1em; + height: 1em; + border: 0.55em solid transparent; + border-left-color: var(--doc-font-color); + transform: rotate(-90deg) translateX(50%) translateY(50%); + transform-origin: left; +} + +.doc .source-toolbox .copy-button.clicked .copy-toast { + opacity: 1; + transition: none; +} + +.doc .language-console .hljs-meta { + user-select: none; +} + +.doc .dlist dt { + font-style: italic; +} + +.doc .dlist dd { + margin: 0 0 0 1.5rem; +} + +.doc .dlist dd + dt, +.doc .dlist dd > p:first-child { + margin-top: 0.5rem; +} + +.doc td.hdlist1, +.doc td.hdlist2 { + padding: 0.5rem 0 0; + vertical-align: top; +} + +.doc tr:first-child > .hdlist1, +.doc tr:first-child > .hdlist2 { + padding-top: 0; +} + +.doc td.hdlist1 { + font-weight: var(--body-font-weight-bold); + padding-right: 0.25rem; +} + +.doc td.hdlist2 { + padding-left: 0.25rem; +} + +.doc .colist { + font-size: calc(16 / var(--rem-base) * 1rem); + margin: 0.25rem 0 -0.25rem; +} + +.doc .colist > table > tr > :first-child, +.doc .colist > table > tbody > tr > :first-child { + padding: 0.25em 0.5rem 0; + vertical-align: top; +} + +.doc .colist > table > tr > :last-child, +.doc .colist > table > tbody > tr > :last-child { + padding: 0.25rem 0; +} + +.doc .conum[data-value] { + border: 1px solid currentColor; + border-radius: 100%; + display: inline-block; + font-family: var(--body-font-family); + font-size: calc(13.5 / var(--rem-base) * 1rem); + font-style: normal; + line-height: 1.2; + text-align: center; + width: 1.25em; + height: 1.25em; + letter-spacing: -0.25ex; + text-indent: -0.25ex; +} + +.doc .conum[data-value]::after { + content: attr(data-value); +} + +.doc .conum[data-value] + b { + display: none; +} + +.doc hr { + border: solid var(--section-divider-color); + border-width: 2px 0 0; + height: 0; +} + +.doc b.button { + white-space: nowrap; /* effectively ignores hyphens setting */ +} + +.doc b.button::before { + content: "["; + padding-right: 0.25em; +} + +.doc b.button::after { + content: "]"; + padding-left: 0.25em; +} + +.doc kbd { + display: inline-block; + font-size: calc(12 / var(--rem-base) * 1rem); + background: var(--kbd-background); + border: 1px solid var(--kbd-border-color); + border-radius: 0.25em; + box-shadow: 0 1px 0 var(--kbd-border-color), 0 0 0 0.1em var(--body-background) inset; + padding: 0.25em 0.5em; + vertical-align: text-bottom; + white-space: nowrap; /* effectively ignores hyphens setting */ +} + +.doc kbd, +.doc .keyseq { + line-height: 1; +} + +.doc .keyseq { + font-size: calc(16 / var(--rem-base) * 1rem); +} + +.doc .keyseq kbd { + margin: 0 0.125em; +} + +.doc .keyseq kbd:first-child { + margin-left: 0; +} + +.doc .keyseq kbd:last-child { + margin-right: 0; +} + +.doc .menuseq, +.doc .path { + hyphens: none; +} + +.doc .menuseq i.caret::before { + content: "\203a"; + font-size: 1.1em; + font-weight: var(--body-font-weight-bold); + line-height: calc(1 / 1.1); +} + +.doc :not(pre).nowrap { + white-space: nowrap; +} + +.doc .nobreak { + hyphens: none; + word-wrap: normal; +} + +.doc :not(pre).pre-wrap { + white-space: pre-wrap; +} + +#footnotes { + font-size: 0.85em; + line-height: 1.5; + margin: 2rem -0.5rem 0; +} + +.doc td.tableblock > .content #footnotes { + margin: 2rem 0 0; +} + +#footnotes hr { + border-top-width: 1px; + margin-top: 0; + width: 20%; +} + +#footnotes .footnote { + margin: 0.5em 0 0 1em; +} + +#footnotes .footnote + .footnote { + margin-top: 0.25em; +} + +#footnotes .footnote > a:first-of-type { + display: inline-block; + margin-left: -2em; + text-align: right; + width: 1.5em; +} + +/* ---- upstream: pagination.css ---- */ + +nav.pagination { + display: flex; + border-top: 1px solid var(--toolbar-border-color); + line-height: 1; + margin: 2rem -1rem -1rem; + padding: 0.75rem 1rem 0; +} + +nav.pagination span { + display: flex; + flex: 50%; + flex-direction: column; +} + +nav.pagination .prev { + padding-right: 0.5rem; +} + +nav.pagination .next { + margin-left: auto; + padding-left: 0.5rem; + text-align: right; +} + +nav.pagination span::before { + color: var(--toolbar-muted-color); + font-size: 0.75em; + padding-bottom: 0.1em; +} + +nav.pagination .prev::before { + content: "Prev"; +} + +nav.pagination .next::before { + content: "Next"; +} + +nav.pagination a { + font-weight: var(--body-font-weight-bold); + line-height: 1.3; + position: relative; +} + +nav.pagination a::before, +nav.pagination a::after { + color: var(--toolbar-muted-color); + font-weight: normal; + font-size: 1.5em; + line-height: 0.75; + position: absolute; + top: 0; + width: 1rem; +} + +nav.pagination .prev a::before { + content: "\2039"; + transform: translateX(-100%); +} + +nav.pagination .next a::after { + content: "\203a"; +} + +/* ---- upstream: header.css ---- */ + +@media screen and (max-width: 1023.5px) { + html.is-clipped--navbar { + overflow-y: hidden; + } +} + +body { + padding-top: var(--navbar-height); +} + +.navbar { + background: var(--navbar-background); + color: var(--navbar-font-color); + font-size: calc(16 / var(--rem-base) * 1rem); + height: var(--navbar-height); + position: fixed; + top: 0; + width: 100%; + z-index: var(--z-index-navbar); +} + +.navbar a { + text-decoration: none; +} + +.navbar-brand { + display: flex; + flex: auto; + padding-left: 1rem; +} + +.navbar-brand .navbar-item { + color: var(--navbar-font-color); +} + +.navbar-brand .navbar-item:first-child { + align-self: center; + padding: 0; + font-size: calc(22 / var(--rem-base) * 1rem); + flex-wrap: wrap; + line-height: 1; +} + +.navbar-brand .navbar-item:first-child a { + color: inherit; + word-wrap: normal; +} + +.navbar-brand .navbar-item:first-child :not(:last-child) { + padding-right: 0.375rem; +} + +.navbar-brand .navbar-item.search { + flex: auto; + justify-content: flex-end; +} + +#search-input { + color: var(--color-text); + font-family: inherit; + font-size: 0.95rem; + width: 150px; + border: 1px solid var(--color-control-border); + border-radius: 0.1em; + line-height: 1.5; + padding: 0 0.25em; +} + +#search-input:disabled { + background-color: var(--color-surface-hover); + /* disable cursor */ + cursor: not-allowed; + pointer-events: all !important; +} + +#search-input:disabled::placeholder { + color: var(--color-text-muted); +} + +.navbar-burger { + background: none; + border: none; + line-height: 1; + position: relative; + width: 3rem; + padding: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-left: auto; + min-width: 0; +} + +.navbar-burger span { + background-color: var(--navbar-font-color); + height: 1.5px; + width: 1rem; +} + +.navbar-burger:not(.is-active) span { + transition: transform ease-out 0.25s, opacity 0s 0.25s, margin-top ease-out 0.25s 0.25s; +} + +.navbar-burger span + span { + margin-top: 0.25rem; +} + +.navbar-burger.is-active span + span { + margin-top: -1.5px; +} + +.navbar-burger.is-active span:nth-child(1) { + transform: rotate(45deg); +} + +.navbar-burger.is-active span:nth-child(2) { + opacity: 0; +} + +.navbar-burger.is-active span:nth-child(3) { + transform: rotate(-45deg); +} + +.navbar-item, +.navbar-link { + color: var(--navbar-menu-font-color); + display: block; + line-height: var(--doc-line-height); + padding: 0.5rem 1rem; +} + +.navbar-item.has-dropdown { + padding: 0; +} + +.navbar-item .icon { + width: 1.25rem; + height: 1.25rem; + display: block; +} + +.navbar-item .icon img, +.navbar-item .icon svg { + fill: currentColor; + width: inherit; + height: inherit; +} + +.navbar-link { + padding-right: 2.5em; +} + +.navbar-dropdown .navbar-item { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.navbar-dropdown .navbar-item.has-label { + display: flex; + justify-content: space-between; +} + +.navbar-dropdown .navbar-item small { + color: var(--toolbar-muted-color); + font-size: calc(12 / var(--rem-base) * 1rem); +} + +.navbar-divider { + background-color: var(--navbar-menu-border-color); + border: none; + height: 1px; + margin: 0.25rem 0; +} + +.navbar .button { + display: inline-flex; + align-items: center; + background: var(--navbar-button-background); + border: 1px solid var(--navbar-button-border-color); + border-radius: 0.15rem; + height: 1.75rem; + color: var(--navbar-button-font-color); + padding: 0 0.75em; + white-space: nowrap; +} + +@media screen and (max-width: 768.5px) { + .navbar-brand .navbar-item.search { + padding-left: 0; + padding-right: 0; + } +} + +@media screen and (min-width: 769px) { + .navbar-brand .navbar-item.search { + padding-right: 0; + margin-right: 0.5rem; + } + + #search-input { + width: 200px; + } +} + +@media screen and (max-width: 1023.5px) { + .navbar-brand { + height: inherit; + } + + .navbar-brand .navbar-item { + align-items: center; + display: flex; + } + + .navbar-menu { + background: var(--navbar-menu-background); + box-shadow: var(--shadow-menu); + max-height: var(--body-min-height); + overflow-y: auto; + overscroll-behavior: none; + padding: 0.5rem 0; + } + + .navbar-menu:not(.is-active) { + display: none; + } + + .navbar-menu a.navbar-item:hover, + .navbar-menu .navbar-link:hover { + background: var(--navbar-menu_hover-background); + } +} + +@media screen and (min-width: 1024px) { + .navbar-burger { + display: none; + } + + .navbar, + .navbar-menu, + .navbar-end { + display: flex; + } + + .navbar-item, + .navbar-link { + display: flex; + position: relative; + flex: none; + } + + .navbar-item:not(.has-dropdown), + .navbar-link { + align-items: center; + } + + .navbar-item.is-hoverable:hover .navbar-dropdown { + display: block; + } + + .navbar-link::after { + border-width: 0 0 1px 1px; + border-style: solid; + content: ""; + display: block; + height: 0.5em; + pointer-events: none; + position: absolute; + transform: rotate(-45deg); + width: 0.5em; + margin-top: -0.375em; + right: 1.125em; + top: 50%; + } + + .navbar-end > .navbar-item, + .navbar-end .navbar-link { + color: var(--navbar-font-color); + } + + .navbar-end > a.navbar-item:hover, + .navbar-end .navbar-link:hover, + .navbar-end .navbar-item.has-dropdown:hover .navbar-link { + background: var(--navbar_hover-background); + color: var(--navbar-font-color); + } + + .navbar-end .navbar-link::after { + border-color: currentColor; + } + + .navbar-dropdown { + background: var(--navbar-menu-background); + border: 1px solid var(--navbar-menu-border-color); + border-top: none; + border-radius: 0 0 0.25rem 0.25rem; + display: none; + top: 100%; + left: 0; + min-width: 100%; + position: absolute; + } + + .navbar-dropdown .navbar-item { + padding: 0.5rem 3rem 0.5rem 1rem; + white-space: nowrap; + } + + .navbar-dropdown .navbar-item small { + position: relative; + right: -2rem; + } + + .navbar-dropdown .navbar-item:last-child { + border-radius: inherit; + } + + .navbar-dropdown.is-right { + left: auto; + right: 0; + } + + .navbar-dropdown a.navbar-item:hover { + background: var(--navbar-menu_hover-background); + } +} + +/* ---- upstream: footer.css ---- */ + +footer.footer { + background-color: var(--footer-background); + color: var(--footer-font-color); + font-size: calc(15 / var(--rem-base) * 1rem); + line-height: var(--footer-line-height); + padding: 1.5rem; +} + +.footer p { + margin: 0.5rem 0; +} + +.footer a { + color: var(--footer-link-font-color); +} + +/* ---- upstream: highlight.css ---- */ + +/*! Adapted from the GitHub style by Vasily Polovnyov */ +.hljs-comment, +.hljs-quote { + color: var(--syntax-comment); + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-subst { + color: var(--syntax-keyword); + font-weight: var(--monospace-font-weight-bold); +} + +.hljs-number, +.hljs-literal, +.hljs-variable, +.hljs-template-variable, +.hljs-tag .hljs-attr { + color: var(--syntax-number); +} + +.hljs-string, +.hljs-doctag { + color: var(--syntax-string); +} + +.hljs-title, +.hljs-section, +.hljs-selector-id { + color: var(--syntax-function); + font-weight: var(--monospace-font-weight-bold); +} + +.hljs-subst { + font-weight: normal; +} + +.hljs-type, +.hljs-class .hljs-title { + color: var(--code-block-text); + font-weight: var(--monospace-font-weight-bold); +} + +.hljs-tag, +.hljs-name, +.hljs-attribute { + color: var(--syntax-keyword); + font-weight: normal; +} + +.hljs-regexp, +.hljs-link { + color: var(--syntax-string); +} + +.hljs-symbol, +.hljs-bullet { + color: var(--syntax-number); +} + +.hljs-built_in, +.hljs-builtin-name { + color: var(--code-block-text); +} + +.hljs-meta { + color: var(--syntax-annotation); + font-weight: var(--monospace-font-weight-bold); +} + +.hljs-deletion { + background: var(--syntax-removed); +} + +.hljs-addition { + background: var(--syntax-added); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: var(--monospace-font-weight-bold); +} + +/* ---- upstream: print.css ---- */ + +@page { + margin: 0.5in; +} + +@media print { + .hide-for-print { + display: none !important; + } + + html { + font-size: var(--body-font-size--print); + } + + a { + color: inherit !important; + text-decoration: underline; + } + + a.bare, + a[href^="#"], + a[href^="mailto:"] { + text-decoration: none; + } + + tr, + img, + object, + svg { + page-break-inside: avoid; + } + + thead { + display: table-header-group; + } + + pre { + hyphens: none; + white-space: pre-wrap; + } + + body { + padding-top: 2rem; + } + + .navbar { + background: none; + color: inherit; + position: absolute; + } + + .navbar * { + color: inherit !important; + } + + .navbar > :not(.navbar-brand), + .nav-container, + .toolbar, + aside.toc, + nav.pagination { + display: none; + } + + .doc { + color: inherit; + margin: auto; + max-width: none; + padding-bottom: 2rem; + } + + .doc .admonitionblock td.icon { + color-adjust: exact; + } + + .doc .listingblock code[data-lang]::before { + display: block; + } + + footer.footer { + background: none; + border-top: 1px solid var(--panel-border-color); + color: var(--quote-attribution-font-color); + padding: 0.25rem 0.5rem 0; + } + + .footer * { + color: inherit; + } +} + +/* ==== Part 2: Jmix ==== */ + +/* ---- Typefaces ---- */ +/* Fontsource 5.3.0, OFL-1.1 (font/LICENSE-*.txt). Upright Roboto is one variable file per subset for all weights; + the italic is a static 400 file. */ + +@font-face { + font-family: "Roboto"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url(../font/roboto-latin-wght-normal.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Roboto"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url(../font/roboto-latin-ext-wght-normal.woff2) format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "Roboto"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url(../font/roboto-cyrillic-wght-normal.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +@font-face { + font-family: "Roboto"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(../font/roboto-latin-400-italic.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Roboto"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(../font/roboto-latin-ext-400-italic.woff2) format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "Roboto"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(../font/roboto-cyrillic-400-italic.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(../font/jetbrains-mono-latin-400-normal.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(../font/jetbrains-mono-latin-ext-400-normal.woff2) format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(../font/jetbrains-mono-cyrillic-400-normal.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(../font/jetbrains-mono-latin-400-italic.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(../font/jetbrains-mono-latin-ext-400-italic.woff2) format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(../font/jetbrains-mono-cyrillic-400-italic.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +/* ---- Event banner: #jmix-banner.jmix-banner, a .navbar-item inside .navbar-brand after the version dropdown, shown by its event script with an inline style ---- */ + +/* the .navbar-brand prefix ties these rules with part 1's .navbar-brand .navbar-item rules, so source order decides */ + +.navbar-brand .jmix-banner { + display: none; + flex: 1 1 auto; + align-items: center; + align-self: center; + justify-content: center; + min-width: 0; + min-height: 1.8rem; + height: auto; + margin-left: 1rem; + padding: 0.2rem 0.5rem 0.2rem 1rem; + border: 1px solid var(--banner-border); + border-radius: var(--radius-sm); + background-color: var(--banner-background); + color: var(--banner-text); + font-size: 0.85rem; +} + +.navbar-brand .jmix-banner-content { + flex: 1 1 auto; + min-width: 0; + line-height: 1.2; + text-align: center; + white-space: normal; +} + +/* a brand-color dot before the text draws the eye in place of a solid fill; a pseudo-element, so the event markup stays as it is */ +.navbar-brand .jmix-banner-content::before { + content: ""; + display: inline-block; + width: 0.45rem; + height: 0.45rem; + margin-right: 0.5rem; + border-radius: 50%; + background-color: var(--banner-marker); + vertical-align: 0.08em; +} + +.navbar-brand .jmix-banner-link, +.navbar-brand .jmix-banner-link:visited { + color: var(--banner-link); + text-decoration: underline; +} + +.navbar-brand .jmix-banner-link:hover { + color: var(--color-link-hover); +} + +.navbar-brand .jmix-banner-close-button { + display: inline-flex; + align-items: center; + justify-content: center; + flex: none; + width: 1.5rem; + height: 1.5rem; + margin-left: 0.5rem; + padding: 0; + border: none; + border-radius: var(--radius-sm); + background: none; + color: var(--banner-close); + font-size: 1.2rem; + line-height: 1; + cursor: pointer; +} + +.navbar-brand .jmix-banner-close-button:hover { + background-color: var(--banner-border); + color: var(--banner-text); +} + +@media screen and (max-width: 1400px) { + .navbar-brand .jmix-banner { + font-size: 0.8rem; + } +} + +@media screen and (max-width: 1250px) { + .navbar-brand .jmix-banner { + padding: 0.2rem 0.25rem 0.2rem 0.5rem; + font-size: 0.72rem; + } + + .navbar-brand .jmix-banner-close-button { + margin-left: 0.25rem; + font-size: 1rem; + } +} + +@media screen and (max-width: 1150px) { + .navbar-brand .jmix-banner { + font-size: 0.65rem; + } +} + +/* the banner text changes with every event; below 1300px a long one takes three lines and soon overflows the header */ +@media screen and (max-width: 1300px) { + .navbar-brand .jmix-banner { + /* event scripts show the banner with an inline style; only !important hides it on narrow screens */ + display: none !important; + } +} + +/* ---- Content additions: roles and buttons used by the pages ---- */ + +.doc { + hyphens: none; +} + +.jmix-ui-live-demo-container { + float: right; + padding: 1px; +} + +.doc .paragraph.since { + margin-top: 0.5rem; +} + +.doc .paragraph.since p { + display: inline-block; + margin: 0; + padding: 0.15em 0.65em; + border-radius: 999px; + background: var(--color-accent-tint); + color: var(--color-accent); + font-size: 0.75rem; + font-weight: 500; + letter-spacing: normal; + line-height: 1.4; + white-space: nowrap; +} + +.doc .admonitionblock.addon-component + .jmix-ui-live-demo-container { + margin-top: 0.5rem; +} + +a.live-demo-btn, +a.vaadin-docs-btn, +a.mdn-docs-btn, +a.echarts-docs-btn { + margin-left: 20px; + margin-bottom: 10px; + border-radius: var(--radius-md); + font-weight: bold; + text-decoration: none; +} + +a.live-demo-btn { + padding: 8px 20px; + background-color: var(--live-demo-background); + color: var(--live-demo-text); +} + +a.live-demo-btn:hover { + background-color: var(--live-demo-background-hover); + color: var(--live-demo-text); +} + +a.vaadin-docs-btn, +a.mdn-docs-btn, +a.echarts-docs-btn { + padding: 6px 15px; + font-size: smaller; +} + +a.vaadin-docs-btn { + background-color: var(--vaadin-docs-background); + color: var(--vaadin-docs-text); +} + +a.vaadin-docs-btn:hover { + background-color: var(--vaadin-docs-background-hover); + color: var(--vaadin-docs-text); +} + +a.mdn-docs-btn { + background-color: var(--mdn-docs-background); + color: var(--mdn-docs-text); +} + +a.mdn-docs-btn:hover { + background-color: var(--mdn-docs-background-hover); + color: var(--mdn-docs-text); +} + +a.echarts-docs-btn, +a.echarts-docs-btn:hover { + background-color: var(--echarts-docs-background); + color: var(--echarts-docs-text); +} + +@media (forced-colors: active) { + /* their fill is what shows them as buttons and as a pill */ + a.live-demo-btn, + a.vaadin-docs-btn, + a.mdn-docs-btn, + a.echarts-docs-btn { + border: 1px solid LinkText; + } + + .doc .paragraph.since p { + border: 1px solid CanvasText; + } +} + +/* ---- Header ---- */ + +.navbar { + border-bottom: 1px solid var(--color-line); +} + +.navbar-brand .navbar-item:first-child { + font-size: 1rem; + font-weight: 600; + letter-spacing: -0.005em; +} + +.navbar-logo-center { + fill: var(--header-logo-center); +} + +.navbar-brand .vertical-line { + margin: 0.95rem 0.875rem; + border-left: 1px solid var(--color-line); +} + +@media screen and (min-width: 1024px) { + .navbar-brand { + flex-grow: 1; + } + + .navbar-menu { + flex-grow: 0; + } +} + +.header-btn { + display: flex; + gap: 0.25rem; + align-items: center; + margin-right: 0.75rem; +} + +.header-icon-link { + display: grid; + place-items: center; + width: 2.25rem; + height: 2.25rem; + border-radius: var(--radius-lg); + color: var(--header-text); +} + +.header-icon-link:hover { + background: var(--navbar_hover-background); +} + +.header-icon-link svg { + width: auto; + height: 1.25rem; + fill: currentColor; + /* keeps the AI Assistant mask intact; the forced colors block below then names the icon color itself */ + forced-color-adjust: none; +} + +@media screen and (max-width: 1023.5px) { + .header-btn { + flex-wrap: wrap; + justify-content: center; + margin: 0.5rem 0; + } +} + +@media screen and (max-width: 420px) { + .navbar-brand .navbar-item:first-child { + font-size: 0.75rem; + } +} + +@media screen and (max-width: 340px) { + .navbar-brand .vertical-line { + display: none; + } +} + +@media (forced-colors: active) { + /* with forced-color-adjust: none the icon inherits the author color of its link, not the forced one */ + .header-icon-link svg { + color: LinkText; + } + + /* the navy center of the logo would vanish on a dark Canvas */ + .navbar-logo-center { + fill: CanvasText; + } + + /* upstream draws the burger's lines with background-color, which forced colors removes */ + .navbar-burger span { + forced-color-adjust: none; + background-color: ButtonText; + } +} + +/* ---- Accessibility ---- */ + +/* the ring needs room when the browser scrolls a focused element into view (scroll-margin), and + the fixed header and the sticky toolbar cover the top of the window (scroll-padding-top); + site.js scrolls its own fragment jumps with scrollTo, which ignores both */ +:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 2px; + scroll-margin: 6px; +} + +html { + scroll-padding-top: calc(var(--navbar-height) + var(--toolbar-height) + 1rem); +} + +/* these fill a scrolling list or touch the window edge, where a ring outside the box is cut off: it goes inside */ +.navbar-burger:focus-visible, +.nav-toggle:focus-visible, +.nav-item-toggle:focus-visible, +.nav-panel-explore .context:focus-visible, +.sidebar.toc .toc-menu a:focus-visible { + outline-offset: -2px; +} + +#main-content:focus { + /* the skip link target is a region, not a control */ + outline: none; +} + +.skip-link { + position: fixed; + z-index: 100; + top: 0.5rem; + left: 0.75rem; + padding: 0.5rem 0.875rem; + border: 1px solid var(--color-line); + border-radius: var(--radius-md); + background: var(--color-surface); + box-shadow: var(--shadow-menu); + color: var(--color-heading); + font-weight: 600; + text-decoration: none; + translate: 0 -200%; +} + +.skip-link:focus-visible { + translate: 0 0; +} + +/* the copy button is visible and reachable by keyboard, not only on hover */ +.doc .source-toolbox { + visibility: visible; +} + +.doc .source-toolbox .copy-icon { + display: none; +} + +.doc .source-toolbox .copy-button::before { + content: ""; + flex: none; + width: 1em; + height: 1em; + background-color: currentColor; + mask: var(--icon-copy) center / contain no-repeat; +} + +/* the explore panel toggle is a button now */ +.nav-panel-explore .context { + width: 100%; + border: 0; + background: transparent; + font: inherit; + text-align: left; +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } +} + +@media (forced-colors: active) { + .doc .source-toolbox .copy-button::before { + forced-color-adjust: none; + background-color: ButtonText; + } +} + +/* ---- Navigation ---- */ + +@media screen and (min-width: 1024px) { + .nav { + border-right: 1px solid var(--color-line); + } +} + +.nav-menu { + padding: 1rem 0.75rem 3rem 1.25rem; +} + +.nav-menu h3.title { + padding: 0 0.5rem 0.375rem; + color: var(--color-heading); + font-size: 0.8rem; + font-weight: 600; +} + +.nav-menu h3.title a:hover { + text-decoration: none; +} + +.nav-item { + margin-top: 1px; +} + +.nav-list { + margin-left: 0.875rem; +} + +.nav-menu > .nav-list { + margin-left: 0.25rem; +} + +/* .nav prefix: upstream .nav a { color: inherit } is (0,1,1) */ +.nav .nav-link, +.nav .nav-text { + display: block; + padding: 0.3rem 0.5rem; + border-radius: var(--radius-md); + color: var(--color-text-nav); +} + +.nav-link:hover { + background: var(--color-surface-hover); + color: var(--color-heading); + text-decoration: none; +} + +.is-current-page > .nav-link { + background: var(--color-accent-tint); + color: var(--color-accent); + font-weight: 600; +} + +.nav-item-toggle { + width: 24px; + height: 24px; + margin: 3px 0 0 -24px; + border-radius: 5px; + background: none; +} + +.nav-item-toggle::before { + content: ""; + display: block; + width: 100%; + height: 100%; + background-color: var(--color-text-muted); + mask: var(--icon-chevron) center / 14px no-repeat; +} + +.nav-item-toggle:hover { + background: var(--color-surface-hover); +} + +/* "expand all" at the top of the menu. Upstream hides it with visibility until the menu is + hovered, which also takes it out of the tab order; here opacity hides it, and visibility + follows the nav, which part 1 hides while it is closed below 1024px. The .nav-menu prefix + beats upstream .nav-menu-toggle.is-active (0,2,0), which sets a background image. */ +.nav-menu .nav-menu-toggle { + width: 24px; + height: 24px; + margin: -2px -0.25rem 0 0.5rem; + border-radius: 5px; + background: none; + visibility: inherit; + opacity: 0; +} + +.nav-menu .nav-menu-toggle::before { + content: ""; + display: block; + width: 100%; + height: 100%; + background-color: var(--color-text-muted); + mask: var(--icon-unfold) center / 16px no-repeat; +} + +.nav-menu .nav-menu-toggle.is-active::before { + mask-image: var(--icon-fold); +} + +.nav-panel-menu.is-active:hover .nav-menu-toggle, +.nav-menu .nav-menu-toggle:focus-visible { + opacity: 1; +} + +.nav-menu .nav-menu-toggle:hover { + background: var(--color-surface-hover); +} + +/* no hover on touch screens: an invisible button would still take taps */ +@media (hover: none) { + .nav-menu .nav-menu-toggle { + opacity: 1; + } +} + +/* explore panel: component and version switcher at the bottom of the nav */ + +/* font-size and line-height again: the button reset in Accessibility (font: inherit) clears part 1's */ +.nav-panel-explore .context { + padding: 0 0.75rem 0 1rem; + border-top: 1px solid var(--color-line); + box-shadow: none; + background: var(--color-surface); + color: var(--color-text-muted); + font-size: 0.8rem; + line-height: 1; + cursor: pointer; +} + +.nav-panel-explore .context:hover { + background: var(--color-surface-hover); + color: var(--color-heading); +} + +.nav-panel-explore .context .title { + color: var(--color-heading); + font-weight: 500; +} + +.nav-panel-explore .context .version::after { + width: 1rem; + height: 1rem; + background: currentColor; + mask: var(--icon-chevron) center / 12px no-repeat; + rotate: -90deg; +} + +.nav-panel-explore.is-active .context .version::after { + rotate: 90deg; +} + +.nav-panel-explore .components { + padding: 0.875rem 1rem 0; + border-top: 1px solid var(--color-line); + box-shadow: none; + background: var(--color-surface-subtle); +} + +.nav-panel-explore .component .title { + color: var(--color-heading); + font-weight: 600; + text-decoration: none; +} + +.nav-panel-explore .versions { + gap: 0.375rem; + margin: 0.5rem 0 0; +} + +.nav-panel-explore .component .version { + margin: 0; +} + +.nav-panel-explore .component .version a { + padding: 0.3em 0.7em; + border: 1px solid var(--color-line-strong); + border-radius: var(--radius-md); + background: var(--color-surface); + color: var(--color-text); + font-size: 0.78rem; + font-weight: 500; + opacity: 1; +} + +.nav-panel-explore .component .version a:hover { + border-color: var(--color-accent); + color: var(--color-accent); + text-decoration: none; +} + +.nav-panel-explore .component .is-current a { + border-color: var(--color-accent); + background: var(--color-accent); + color: var(--color-text-on-accent); +} + +.nav-panel-explore .component .is-current a:hover { + border-color: var(--color-accent); + color: var(--color-text-on-accent); +} + +@media (prefers-reduced-motion: no-preference) { + .nav-item-toggle { + transition: transform 0.15s; + } + + .nav-panel-explore .context .version::after { + transition: rotate 0.15s; + } +} + +@media (forced-colors: active) { + .nav-item-toggle::before, + .nav-menu .nav-menu-toggle::before, + .nav-panel-explore .context .version::after { + forced-color-adjust: none; + background-color: ButtonText; + } + + .is-current-page > .nav-link, + .nav-panel-explore .component .is-current a { + border: 1px solid Highlight; + } + + /* the version pills have a border already; the nav link gives the border's 1px back */ + .is-current-page > .nav-link { + padding: calc(0.3rem - 1px) calc(0.5rem - 1px); + } +} + +/* ---- Toolbar ---- */ + +.toolbar { + border-bottom: 1px solid var(--color-line); + box-shadow: none; + font-size: 0.8rem; +} + +/* the border takes 1px from the content box and the nav toggle (shown below 1024px) is as tall as the + toolbar: it would overflow and slide 0.5px under the fixed header, which covers part of its focus ring */ +.nav-toggle { + height: 100%; +} + +/* the toolbar's nav toggle below 1024px: mask icons instead of upstream's dark menu.svg and + back.svg, so the icon follows the text color and forced colors */ +.nav-toggle, +.nav-toggle.is-active { + background: none; + color: var(--color-text-muted); +} + +.nav-toggle:hover { + color: var(--color-heading); +} + +.nav-toggle::before { + content: ""; + display: block; + width: 1.25rem; + height: 1.25rem; + margin: 0 auto; + background-color: currentColor; + mask: var(--icon-menu) center / contain no-repeat; +} + +.nav-toggle.is-active::before { + mask-image: var(--icon-back); +} + +.home-link, +.home-link.is-current, +.home-link:hover { + display: grid; + place-items: center; + width: 1.75rem; + height: 1.75rem; + margin: 0 0 0 1rem; + border-radius: var(--radius-md); + background: none; +} + +.home-link::before { + content: ""; + width: 1rem; + height: 1rem; + background-color: currentColor; + mask: var(--icon-home) center / contain no-repeat; +} + +.home-link:hover { + background: var(--color-surface-hover); + color: var(--color-heading); +} + +/* .toolbar prefix: upstream a + .breadcrumbs is (0,1,1) */ +.toolbar .breadcrumbs { + padding-left: 0.375rem; +} + +.breadcrumbs li::after { + color: var(--color-line-strong); +} + +.breadcrumbs li:last-of-type { + color: var(--color-text); +} + +.breadcrumbs a:hover { + color: var(--color-heading); +} + +.toolbar .edit-this-page { + padding-right: 1rem; +} + +.toolbar .edit-this-page a { + display: inline-flex; + gap: 0.375rem; + align-items: center; + color: var(--color-text-muted); +} + +.toolbar .edit-this-page a::before { + content: ""; + width: 0.875rem; + height: 0.875rem; + background-color: currentColor; + mask: var(--icon-edit) center / contain no-repeat; +} + +.toolbar .edit-this-page a:hover { + color: var(--color-heading); + text-decoration: none; +} + +@media (forced-colors: active) { + .home-link::before, + .toolbar .edit-this-page a::before { + forced-color-adjust: none; + background-color: LinkText; + } + + .nav-toggle::before { + forced-color-adjust: none; + background-color: ButtonText; + } +} + +/* ---- Table of contents ---- */ + +.toc .toc-menu h3 { + font-weight: 600; +} + +@media screen and (min-width: 1024px) { + .toc .toc-menu h3, + .toc .toc-menu ul { + font-size: 0.78rem; + } +} + +.toc .toc-menu a { + padding: 0.3rem 0 0.3rem 0.75rem; + border-left-width: 1px; + overflow-wrap: break-word; +} + +.toc .toc-menu li[data-level="2"] a { + padding-left: 1.5rem; +} + +.toc .toc-menu li[data-level="3"] a { + padding-left: 2.25rem; +} + +.toc .toc-menu a:hover { + color: var(--color-heading); +} + +.toc .toc-menu a.is-active { + margin-left: -0.5px; + border-left: 2px solid var(--color-accent); + color: var(--color-accent); + font-weight: 500; +} + +@media (forced-colors: active) { + .toc .toc-menu a.is-active { + border-left-color: Highlight; + } +} + +/* ---- Article ---- */ + +.doc p { + text-wrap: pretty; +} + +.doc :is(h1, h2, h3, h4, h5, h6) { + letter-spacing: -0.01em; + text-wrap: balance; +} + +.doc > h1.page:first-child { + margin: 2.25rem 0 1.25rem; + font-size: 2.125rem; + font-weight: 700; + letter-spacing: -0.02em; + line-height: 1.2; +} + +.doc h2:not(.discrete) { + margin: 0; + padding: 0; + border-bottom: 0; + font-size: 1.5rem; + line-height: 1.3; +} + +#preamble + .sect1, +.doc .sect1 + .sect1 { + margin-top: 3rem; +} + +/* the TOC is embedded after the h1 below 1024px; from 1024px up it is hidden, and + would still match it */ +@media screen and (max-width: 1023.5px) { + .doc > aside.toc.embedded + .sect1 { + margin-top: 3rem; + } +} + +.doc h3 { + font-size: 1.1875rem; +} + +.doc .sect2 { + margin-top: 2rem; +} + +.doc h4 { + font-size: 1rem; +} + +.doc :is(h1, h2, h3, h4, h5, h6) .anchor { + color: var(--color-text-muted); + font-weight: 400; +} + +.doc :is(h1, h2, h3, h4, h5, h6) .anchor::before { + content: "#"; +} + +/* links in running text do not rely on color alone */ +.doc a:not(.anchor, [class$="-btn"]) { + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 0.2em; + text-decoration-color: color-mix(in oklab, currentColor 35%, transparent); +} + +.doc a:not(.anchor, [class$="-btn"]):hover { + text-decoration-color: currentColor; +} + +.doc :is(h1, h2, h3, h4, h5, h6) a:not(.anchor), +.doc .toc a { + text-decoration: none; +} + +/* part 1 fills only p, thead and colist code; this covers code in dt, summary and block titles too */ +.doc :not(pre) > code, +.doc .colist > table code { + padding: 0.1em 0.35em; + border: 1px solid var(--color-line); + border-radius: var(--radius-sm); + background: var(--code-background); + color: var(--code-font-color); + font-size: 0.86em; +} + +.doc :is(h1, h2, h3, h4, h5, h6) code { + padding: 0; + border: 0; + background: none; + color: inherit; +} + +@media (forced-colors: active) { + .doc :not(pre) > code, + .doc .colist > table code { + border-color: GrayText; + } +} + +/* ---- Code blocks ---- */ + +.doc pre { + line-height: 1.6; +} + +/* JetBrains Mono ships programming ligatures; IntelliJ IDEA keeps them off by default, and readers copy this code */ +.doc pre, +.doc code { + font-variant-ligatures: none; +} + +.doc .listingblock pre:not(.highlight), +.doc .literalblock pre, +.doc pre.highlight > code { + padding: 0.9rem 1.125rem; + border: 1px solid var(--color-line); + border-radius: var(--radius-lg); + box-shadow: none; + color: var(--code-block-text); + font-size: 0.78rem; +} + +/* upstream sets the size of a pre in an admonition (.doc .admonitionblock td.content pre, 15px, (0,3,2)) and in a table + cell (.doc table.tableblock pre, inherit, (0,2,2)): the first beats both plain members of the shared rule, the + second only .doc .literalblock pre (0,2,1) */ +.doc :is(.admonitionblock td.content, table.tableblock) :is(.listingblock pre:not(.highlight), .literalblock pre) { + font-size: 0.78rem; +} + +/* most block titles are file names, so a titled block reads as an editor tab */ +.doc .listingblock > .title { + position: relative; + z-index: 1; + top: 1px; + display: inline-block; + margin: 0; + padding: 0.4rem 0.8rem 0.35rem; + border: 1px solid var(--color-line); + border-bottom: 0; + border-radius: var(--radius-lg) var(--radius-lg) 0 0; + background: var(--code-block-background); + color: var(--color-text); + font-size: 0.75rem; + font-style: normal; + font-weight: 500; + letter-spacing: 0; +} + +.doc .listingblock > .title + .content :is(pre.highlight > code, pre:not(.highlight)) { + border-top-left-radius: 0; +} + +.doc .source-toolbox { + top: 0.45rem; + right: 0.5rem; + gap: 0.25rem; + align-items: center; + color: var(--color-text-muted); + font: 400 0.7rem/1 var(--monospace-font-family); +} + +.doc .source-toolbox .source-lang { + letter-spacing: 0; + text-transform: none; +} + +.doc .source-toolbox > :not(:last-child)::after { + content: none; +} + +.doc .source-toolbox .copy-button { + justify-content: center; + width: 1.75rem; + height: 1.75rem; + border-radius: var(--radius-md); + font-size: 1rem; +} + +.doc .source-toolbox .copy-button::before { + width: 0.9rem; + height: 0.9rem; +} + +.doc .source-toolbox .copy-button:hover { + background: var(--color-surface-hover); + color: var(--color-heading); +} + +.doc .source-toolbox .copy-toast { + position: absolute; + top: 100%; + margin-top: 0.375rem; + background: var(--color-heading); + color: var(--color-text-on-accent); + font: 400 0.7rem/1 var(--body-font-family); +} + +.doc .source-toolbox .copy-toast::after { + border-left-color: var(--color-heading); +} + +/* code-toolbox.js marks blocks whose first line would run under the always-visible toolbox: + their code starts below it */ +@media screen { + .doc pre.highlight.first-line-under-toolbox > code { + padding-top: 2.3rem; + } +} + +@media print { + /* a copy button means nothing on paper, and the toolbox would cover printed code */ + .doc .source-toolbox { + display: none; + } +} + +.doc .conum[data-value] { + width: 1.35em; + height: 1.35em; + border: 0; + border-radius: 50%; + background: var(--color-heading); + color: var(--color-text-on-accent); + font: 600 0.68rem/1.35em var(--body-font-family); + letter-spacing: 0; + text-indent: 0; + vertical-align: 0.1em; +} + +.doc pre .conum[data-value] { + font-size: 0.62rem; +} + +.doc .colist { + margin-top: 0.5rem; +} + +.doc .colist > table > tbody > tr > :first-child { + padding-top: 0.3em; +} + +/* syntax colors: highlight.js 10.7.2 classes */ +.doc :is(.hljs-keyword, .hljs-literal, .hljs-selector-tag, .hljs-section) { + color: var(--syntax-keyword); + font-weight: 400; +} + +/* XML and HTML tags: like keywords in IntelliJ's light scheme, yellow in its dark one */ +.doc :is(.hljs-tag, .hljs-name) { + color: var(--syntax-tag); + font-weight: 400; +} + +.doc :is(.hljs-string, .hljs-regexp, .hljs-link) { + color: var(--syntax-string); +} + +.doc :is(.hljs-number, .hljs-symbol, .hljs-bullet) { + color: var(--syntax-number); +} + +.doc :is(.hljs-comment, .hljs-quote) { + color: var(--syntax-comment); + font-style: italic; +} + +.doc .hljs-doctag { + color: var(--syntax-comment); + font-weight: 600; +} + +.doc .hljs-meta { + color: var(--syntax-annotation); + font-weight: 400; +} + +/* the XML prolog and DOCTYPE take the tag color, so a whole XML block stays in one tag color */ +.doc .language-xml .hljs-meta { + color: var(--syntax-tag); +} + +.doc :is(.hljs-title, .hljs-selector-class, .hljs-selector-id) { + color: var(--syntax-function); + font-weight: 400; +} + +/* the class title selector gives every member of this list a higher specificity than .hljs-title above */ +.doc :is(.hljs-class .hljs-title, .hljs-type, .hljs-params, .hljs-built_in, .hljs-builtin-name, .hljs-subst) { + color: var(--code-block-text); + font-weight: 400; +} + +.doc :is(.hljs-variable, .hljs-template-variable) { + color: var(--syntax-field); +} + +.doc .hljs-attr { + color: var(--syntax-attribute); +} + +.doc :is(.language-json, .language-yaml) .hljs-attr { + color: var(--syntax-field); +} + +.doc .language-properties .hljs-attr, +.doc .hljs-attribute { + color: var(--syntax-property-key); +} + +.doc .hljs-addition { + background: var(--syntax-added); +} + +.doc .hljs-deletion { + background: var(--syntax-removed); +} + +.doc .hljs-strong { + font-weight: 600; +} + +.doc .hljs-emphasis { + font-style: italic; +} + +@media (forced-colors: active) { + .doc .conum[data-value] { + border: 1px solid CanvasText; + } + + .doc .listingblock > .title { + border-color: CanvasText; + } +} + +/* ---- Admonitions ---- */ + +.doc .admonitionblock.note { + --adm-color: var(--note-color); + --adm-background: var(--note-background); + --adm-border: var(--note-border-color); + --adm-accent: var(--note-accent); + --adm-icon: var(--icon-note); +} + +.doc .admonitionblock.tip { + --adm-color: var(--tip-color); + --adm-background: var(--tip-background); + --adm-border: var(--tip-border-color); + --adm-accent: var(--tip-accent); + --adm-icon: var(--icon-tip); +} + +.doc .admonitionblock.warning { + --adm-color: var(--warning-color); + --adm-background: var(--warning-background); + --adm-border: var(--warning-border-color); + --adm-accent: var(--warning-accent); + --adm-icon: var(--icon-warning); +} + +.doc .admonitionblock.important { + --adm-color: var(--important-color); + --adm-background: var(--important-background); + --adm-border: var(--important-border-color); + --adm-accent: var(--important-accent); + --adm-icon: var(--icon-important); +} + +.doc .admonitionblock.caution { + --adm-color: var(--caution-color); + --adm-background: var(--caution-background); + --adm-border: var(--caution-border-color); + --adm-accent: var(--caution-accent); + --adm-icon: var(--icon-caution); +} + +/* the add-on role sits on top of NOTE ("admonitionblock note addon-component"), so it comes last */ +.doc .admonitionblock.addon-component { + --adm-color: var(--addon-color); + --adm-background: var(--addon-background); + --adm-border: var(--addon-border-color); + --adm-accent: var(--addon-accent); + --adm-icon: var(--icon-addon); +} + +.doc .admonitionblock { + margin-top: 1.5rem; +} + +.doc .admonitionblock > table, +.doc .admonitionblock > table > tbody, +.doc .admonitionblock > table > tbody > tr { + display: block; +} + +.doc .admonitionblock > table { + padding: 0.75rem 1.125rem 0.875rem 1rem; + border: 1px solid var(--adm-border); + border-left: 4px solid var(--adm-accent); + border-radius: var(--radius-lg); + background: var(--adm-background); +} + +.doc .admonitionblock td.icon { + position: static; + display: block; + padding: 0 0 0.3rem; + transform: none; + font-size: 0.8rem; +} + +/* i.fa matches the specificity of the upstream per-type pill rules (.doc .admonitionblock td.icon i.icon-note) */ +.doc .admonitionblock td.icon i.fa { + gap: 0.4rem; + height: auto; + padding: 0; + border-radius: 0; + background: none; + color: var(--adm-color); +} + +.doc .admonitionblock td.icon i.fa::before { + content: ""; + width: 1.05rem; + height: 1.05rem; + background-color: currentColor; + mask: var(--adm-icon) center / contain no-repeat; +} + +.doc .admonitionblock td.icon i.fa::after { + letter-spacing: 0; + text-transform: none; +} + +.doc .admonitionblock.addon-component td.icon i.fa::after { + content: "Add-on component"; +} + +.doc .admonitionblock td.content { + display: block; + padding: 0; + background: none; + font-size: 0.9rem; +} + +.doc .admonitionblock td.content p { + font-size: inherit; +} + +/* the rule above is more specific than the badge rule, so a "Since" badge in an admonition would grow to the content size */ +.doc .admonitionblock td.content .paragraph.since p { + font-size: 0.75rem; +} + +@media (forced-colors: active) { + .doc .admonitionblock td.icon i.fa::before { + forced-color-adjust: none; + background-color: CanvasText; + } +} + +/* ---- Images ---- */ + +/* role="light-background": a diagram drawn with dark lines on a transparent background keeps a light plate, + which the dark theme needs; screenshots bring their own background and stay as they are */ +.doc :is(.imageblock, .image).light-background img { + border-radius: var(--radius-sm); + background-color: var(--image-plate-background); +} + +@media (forced-colors: active) { + /* a dark forced colors palette would replace the plate with Canvas */ + .doc :is(.imageblock, .image).light-background img { + forced-color-adjust: none; + } +} + +/* ---- Tables and other blocks ---- */ + +/* the line colors come from tokens; the authors' grid and frame options keep their effect */ +.doc table.tableblock td, +.doc table.tableblock th { + padding: 0.55rem 0.75rem; +} + +.doc table.tableblock > thead > tr > th { + background: var(--color-surface-subtle); + color: var(--color-heading); + font-weight: 600; +} + +.doc table.grid-all > thead th, +.doc table.grid-rows > thead th { + border-bottom-width: 1px; + border-bottom-color: var(--color-line-strong); +} + +.doc td.hdlist1 { + color: var(--color-heading); +} + +.doc .sidebarblock { + border: 1px solid var(--color-line); + border-radius: var(--radius-lg); +} + +.doc .exampleblock > .content { + border: 1px solid var(--color-line); + border-radius: var(--radius-lg); +} + +.doc details > summary { + color: var(--color-heading); + font-weight: 500; +} + +.doc kbd { + box-shadow: 0 1px 0 var(--color-line-strong); + font-size: 0.72rem; +} + +.doc .quoteblock, +.doc .verseblock { + border-left-width: 3px; +} + +@media (forced-colors: active) { + .doc .sidebarblock, + .doc .exampleblock > .content { + border-color: CanvasText; + } +} + +/* ---- End of page ---- */ + +nav.pagination { + gap: 1rem; + margin: 3rem 0 0; + padding: 0; + border-top: 0; +} + +nav.pagination :is(.prev, .next)::before { + content: none; +} + +nav.pagination .prev, +nav.pagination .next { + padding: 0; +} + +nav.pagination span { + /* half the row minus half the gap: upstream flex: 50% lets a lone card grow to the full row */ + flex: 0 0 calc(50% - 0.5rem); +} + +/* the span is a column flex box and the link keeps its content height: grow it, so both cards are as tall as the taller title */ +nav.pagination span > a { + flex-grow: 1; +} + +/* .doc prefix: the pagination sits inside article.doc, and the running-text underline in Article, + .doc a:not(.anchor, [class$="-btn"]), is (0,2,1) */ +.doc nav.pagination a { + display: block; + padding: 0.75rem 1rem 0.875rem; + border: 1px solid var(--color-line); + border-radius: var(--radius-lg); + color: var(--color-heading); + font-weight: 600; + line-height: 1.35; + text-decoration: none; +} + +.doc nav.pagination a:hover { + border-color: var(--color-accent); + background: var(--color-accent-tint); + color: var(--color-accent); + text-decoration: none; +} + +nav.pagination .prev a::before, +nav.pagination .next a::before { + position: static; + display: block; + width: auto; + padding-bottom: 0.25rem; + transform: none; + color: var(--color-text-muted); + font-size: 0.75rem; + font-weight: 400; + line-height: 1.3; +} + +nav.pagination .prev a::before { + content: "Previous"; +} + +nav.pagination .next a::before { + content: "Next"; +} + +nav.pagination .next a::after { + content: none; +} + +@media screen { + footer.footer { + padding: 1.25rem 2rem; + border-top: 1px solid var(--color-line); + font-size: 0.75rem; + } + + .footer p { + margin: 0.25rem 0; + } +} + +@media (forced-colors: active) { + .doc nav.pagination a { + border-color: LinkText; + } +} + +/* ---- Chat widget ---- */ + +/* The DocsBot chat widget, injected by Google Tag Manager, mounts into #docsbotai-root and draws itself for a light + page. color-scheme is inherited, so without this the dark theme would turn the parts it leaves to the browser + (fields, placeholders, scrollbars) dark inside its light panel. */ +#docsbotai-root { + color-scheme: light; +} diff --git a/content/supplemental/css/tokens.css b/content/supplemental/css/tokens.css new file mode 100644 index 00000000..18ded7c5 --- /dev/null +++ b/content/supplemental/css/tokens.css @@ -0,0 +1,382 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL + * was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Custom properties of the docs UI. Started from src/css/vars.css of antora-ui-default at revision 0e38223a; + * tier 3 keeps the upstream names, so part 1 of site.css reads them unchanged. + * + * Tier 1: palette. Tier 2: semantic roles. Tier 3: components. + * Outside this file colors are written only as var(--…) (node tools/check-css.mjs). + * The dark theme is the block at the end of this file (screen only). It sets the tier 2 colors and shadow, and + * the tier 3 colors that must differ in the dark theme; node tools/check-css.mjs checks that it sets every + * --color-* and --shadow-* token. + */ + +:root { + color-scheme: light; + + /* ---- Tier 1: palette ---- */ + + /* brand book */ + --jmix-space: #17124b; + --jmix-fiesta: #fc1264; + --jmix-sun: #fdb42b; + --jmix-sky: #25cde3; + --jmix-grace: #22d685; + + /* accent violet: 700 is the current docs violet, 500 the jmix.io link color, the other steps are derived; + 950, 900 and 300 serve the dark theme */ + --jmix-violet-950: #2b2650; + --jmix-violet-900: #3a3470; + --jmix-violet-800: #261d7d; + --jmix-violet-700: #342a98; + --jmix-violet-500: #7b6dff; + --jmix-violet-300: #a99bff; + --jmix-violet-200: #d3cdf8; + --jmix-violet-50: #f0eeff; + + --gray-900: #2a2c33; + --gray-700: #3a3d46; + --gray-600: #5c606b; + --gray-500: #8a8f99; + --gray-300: #cfd3da; + --gray-200: #e4e6eb; + --gray-100: #eff1f4; + --gray-50: #f6f7f9; + --white: #fff; + --black: #000; + + /* "Ink": the dark theme's grays, with a slight violet cast; derived */ + --ink-950: #17171d; + --ink-900: #1f1f27; + --ink-850: #272730; + --ink-800: #2e2e39; + --ink-700: #3f3f4c; + --ink-500: #6e6e80; + --ink-400: #9e9eb1; + --ink-300: #c6c6d2; + --ink-200: #d8d8e2; + --ink-50: #f2f1f9; + + /* ---- Tier 2: semantic roles ---- */ + + --color-text: var(--gray-900); + --color-heading: var(--jmix-space); + --color-text-nav: var(--gray-700); + --color-text-muted: var(--gray-600); + --color-text-on-accent: var(--white); + --color-link: var(--jmix-violet-700); + --color-link-hover: var(--jmix-violet-800); + --color-accent: var(--jmix-violet-700); + --color-accent-tint: var(--jmix-violet-50); + --color-accent-line: var(--jmix-violet-200); + --color-focus: var(--jmix-violet-700); + --color-surface: var(--white); + --color-surface-subtle: var(--gray-50); + --color-surface-hover: var(--gray-100); + --color-line: var(--gray-200); + --color-line-strong: var(--gray-300); + --color-control-border: var(--gray-500); + --color-overlay: color-mix(in srgb, var(--jmix-space) 30%, transparent); + --shadow-menu: 0 10px 28px color-mix(in srgb, var(--jmix-space) 14%, transparent), 0 2px 6px color-mix(in srgb, var(--jmix-space) 8%, transparent); + + --radius-lg: 8px; + --radius-md: 6px; + --radius-sm: 4px; + + /* ---- Tier 3: components ---- */ + + /* header */ + --header-background: var(--color-surface); + --header-text: var(--color-heading); + --header-logo-center: var(--jmix-space); + + /* code blocks and syntax colors, IntelliJ IDEA Light-like; comment and annotation darkened to 4.5:1 */ + --code-block-background: #f7f8fa; + --code-block-text: #080808; + --inline-code-text: #1f2128; + --syntax-keyword: #0033b3; + --syntax-string: #067d17; + --syntax-number: #1750eb; + --syntax-comment: #6f6f6f; + --syntax-annotation: #7a6a0a; + --syntax-function: #00627a; + --syntax-field: #871094; + --syntax-attribute: #174ad4; + --syntax-property-key: #083080; + /* XML and HTML tags: IntelliJ's light scheme draws them like keywords, its dark scheme in yellow */ + --syntax-tag: #0033b3; + --syntax-added: #e6f4ea; + --syntax-removed: #fce8e6; + + /* admonitions: label and icon, background, border, left edge */ + --note-color: #0a6874; + --note-background: #effbfd; + --note-border-color: #c9eff5; + --note-accent: var(--jmix-sky); + --tip-color: #0d7348; + --tip-background: #effcf5; + --tip-border-color: #c6f0dc; + --tip-accent: var(--jmix-grace); + --warning-color: #8a5300; + --warning-background: #fff8e6; + --warning-border-color: #fbe3ae; + --warning-accent: var(--jmix-sun); + --important-color: #b4104a; + --important-background: #fff1f6; + --important-border-color: #fbcadb; + --important-accent: var(--jmix-fiesta); + --caution-color: #4b3fb8; + --caution-background: #f4f2ff; + --caution-border-color: #dad5fb; + --caution-accent: var(--jmix-violet-500); + --addon-color: var(--jmix-space); + --addon-background: #f4f2ff; + --addon-border-color: #dad5fb; + --addon-accent: var(--jmix-space); + /* upstream names: text of the upstream label pill */ + --caution-on-color: var(--color-text-on-accent); + --important-on-color: var(--color-text-on-accent); + --note-on-color: var(--color-text-on-accent); + --tip-on-color: var(--color-text-on-accent); + --warning-on-color: var(--color-text-on-accent); + + /* event banner and content buttons */ + --banner-background: var(--color-accent-tint); + --banner-border: var(--color-accent-line); + --banner-text: var(--color-text); + --banner-link: var(--color-link); + --banner-close: var(--color-text-muted); + --banner-marker: var(--jmix-fiesta); + --live-demo-background: #fdcb6e; + --live-demo-background-hover: #ffd588; + --live-demo-text: #32346b; + --vaadin-docs-background: #2486f9; + --vaadin-docs-background-hover: #3890f8; + --vaadin-docs-text: #fdfdfd; + --mdn-docs-background: #575757; + --mdn-docs-background-hover: #727272; + --mdn-docs-text: #f8f8f9; + --echarts-docs-background: #b03a5b; + --echarts-docs-text: #fdfdfd; + + /* images: the plate of the light-background role, for diagrams drawn with dark lines on transparency; + white in both themes, so it shows only on the dark one */ + --image-plate-background: var(--white); + + /* mask icons; data URIs because mask images from files are blocked on file:// pages */ + --icon-chevron: url("data:image/svg+xml,"); + --icon-home: url("data:image/svg+xml,"); + --icon-edit: url("data:image/svg+xml,"); + --icon-search: url("data:image/svg+xml,"); + --icon-copy: url("data:image/svg+xml,"); + --icon-note: url("data:image/svg+xml,"); + --icon-tip: url("data:image/svg+xml,"); + --icon-warning: url("data:image/svg+xml,"); + --icon-important: url("data:image/svg+xml,"); + --icon-caution: url("data:image/svg+xml,"); + --icon-addon: url("data:image/svg+xml,"); + --icon-unfold: url("data:image/svg+xml,"); + --icon-fold: url("data:image/svg+xml,"); + --icon-menu: url("data:image/svg+xml,"); + --icon-back: url("data:image/svg+xml,"); + /* sun-moon: Lucide icon (https://lucide.dev/icons/sun-moon), ISC License. + Copyright (c) 2026 Lucide Icons and Contributors + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby + granted, provided that the above copyright notice and this permission notice appear in all copies. */ + --icon-sun-moon: url("data:image/svg+xml,"); + --icon-sun: url("data:image/svg+xml,"); + --icon-moon: url("data:image/svg+xml,"); + --icon-check: url("data:image/svg+xml,"); + --icon-thumb-up: url("data:image/svg+xml,"); + + /* upstream: fonts */ + --rem-base: 18; /* used to compute rem value from desired pixel value (e.g., calc(18 / var(--rem-base) * 1rem) = 18px) */ + --body-font-size: 1.0625em; /* 17px */ + --body-font-size--desktop: 1.125em; /* 18px */ + --body-font-size--print: 0.9375em; /* 15px */ + --body-line-height: 1.15; + --body-font-color: var(--color-text); + --body-font-family: "Roboto", system-ui, sans-serif; + --body-font-weight-bold: 600; + --monospace-font-family: "JetBrains Mono", ui-monospace, monospace; + --monospace-font-weight-bold: 600; + /* upstream: base */ + --body-background: var(--color-surface); + --panel-background: var(--color-surface-subtle); + --panel-border-color: var(--color-line); + --scrollbar-track-color: var(--color-surface-subtle); + --scrollbar-thumb-color: var(--color-line-strong); + --scrollbar_hover-thumb-color: var(--color-control-border); + /* upstream: navbar */ + --navbar-background: var(--header-background); + --navbar-font-color: var(--header-text); + --navbar_hover-background: var(--color-surface-hover); + --navbar-button-background: var(--color-surface); + --navbar-button-border-color: var(--color-line); + --navbar-button-font-color: var(--color-text); + --navbar-menu-border-color: var(--color-line); + --navbar-menu-background: var(--color-surface); + --navbar-menu-font-color: var(--color-text); + --navbar-menu_hover-background: var(--color-surface-hover); + /* upstream: nav */ + --nav-background: var(--color-surface); + --nav-border-color: var(--color-line); + --nav-line-height: 1.4; + --nav-heading-font-color: var(--color-heading); + --nav-muted-color: var(--color-text-muted); + --nav-panel-divider-color: var(--color-line); + --nav-secondary-background: var(--color-surface-subtle); + /* upstream: toolbar */ + --toolbar-background: var(--color-surface); + --toolbar-border-color: var(--color-line); + --toolbar-font-color: var(--color-text-muted); + --toolbar-muted-color: var(--color-text-muted); + --page-version-menu-background: var(--color-surface-subtle); + --page-version-missing-font-color: var(--color-text-muted); + /* upstream: doc */ + --doc-font-color: var(--color-text); + --doc-font-size: inherit; + --doc-font-size--desktop: calc(17 / var(--rem-base) * 1rem); + --doc-line-height: 1.65; + --doc-margin: 0 auto; + --doc-margin--desktop: 0 2rem; + --heading-font-color: var(--color-heading); + --heading-font-weight: 650; + --alt-heading-font-weight: 650; + --section-divider-color: var(--color-line); + --link-font-color: var(--color-link); + --link_hover-font-color: var(--color-link-hover); + --link_unresolved-font-color: var(--important-color); + --abstract-background: var(--color-surface-subtle); + --abstract-font-color: var(--color-text); + --abstract-border-color: var(--color-line); + --admonition-background: var(--color-surface-subtle); + --admonition-label-font-weight: 650; + --caption-font-color: var(--color-text-muted); + --caption-font-style: normal; + --caption-font-weight: 500; + --code-background: var(--color-surface-subtle); + --code-font-color: var(--inline-code-text); + --example-background: var(--color-surface); + --example-border-color: var(--color-line); + --kbd-background: var(--color-surface); + --kbd-border-color: var(--color-line-strong); + --pre-background: var(--code-block-background); + --pre-border-color: var(--color-line); + --pre-annotation-font-color: var(--color-text-muted); + --quote-background: var(--color-surface-subtle); + --quote-border-color: var(--color-line-strong); + --quote-font-color: var(--color-text-muted); + --quote-attribution-font-color: var(--color-text-muted); + --sidebar-background: var(--color-surface-subtle); + --table-border-color: var(--color-line); + --table-stripe-background: var(--color-surface-subtle); + --table-footer-background: linear-gradient(to bottom, var(--color-surface-subtle) 0%, var(--color-surface) 100%); + /* upstream: toc */ + --toc-font-color: var(--color-text-muted); + --toc-heading-font-color: var(--color-heading); + --toc-border-color: var(--color-line); + --toc-line-height: 1.35; + /* upstream: footer */ + --footer-line-height: var(--doc-line-height); + --footer-background: var(--color-surface); + --footer-font-color: var(--color-text-muted); + --footer-link-font-color: var(--color-text-muted); + /* upstream: dimensions and positioning; the toolbar height and the wide TOC width differ from upstream */ + --navbar-height: calc(63 / var(--rem-base) * 1rem); + --toolbar-height: calc(49.5 / var(--rem-base) * 1rem); + --drawer-height: var(--toolbar-height); + --body-top: var(--navbar-height); + --body-min-height: calc(100vh - var(--body-top)); + --nav-height: calc(var(--body-min-height) - var(--toolbar-height)); + --nav-height--desktop: var(--body-min-height); + --nav-panel-menu-height: calc(100% - var(--drawer-height)); + --nav-panel-explore-height: calc(50% + var(--drawer-height)); + --nav-width: calc(270 / var(--rem-base) * 1rem); + --toc-top: calc(var(--body-top) + var(--toolbar-height)); + --toc-height: calc(100vh - var(--toc-top) - 2.5rem); + --toc-width: calc(162 / var(--rem-base) * 1rem); + --toc-width--widescreen: calc(252 / var(--rem-base) * 1rem); + --doc-max-width: calc(720 / var(--rem-base) * 1rem); + --doc-max-width--desktop: calc(828 / var(--rem-base) * 1rem); + /* upstream: stacking */ + --z-index-nav: 1; + --z-index-toolbar: 2; + --z-index-page-version-menu: 3; + --z-index-navbar: 4; +} + +/* ---- Dark theme ---- */ +/* data-theme on comes from the inline script in partials/head-styles.hbs and from js/theme-menu.js. + Screen only, so print keeps the light values. Text colors reach 4.5:1 and the focus ring 3:1 on every surface + (a comment inside a diff line is the known exception). tools/ui-audit.mjs checks a sample of pairs in both + themes. */ +@media screen { + :root[data-theme="dark"] { + color-scheme: dark; + + /* tier 2 */ + --color-text: var(--ink-200); + --color-heading: var(--ink-50); + --color-text-nav: var(--ink-300); + --color-text-muted: var(--ink-400); + --color-text-on-accent: var(--jmix-space); + --color-link: var(--jmix-violet-300); + --color-link-hover: var(--jmix-violet-200); + --color-accent: var(--jmix-violet-300); + --color-accent-tint: var(--jmix-violet-950); + --color-accent-line: var(--jmix-violet-900); + --color-focus: var(--jmix-violet-300); + --color-surface: var(--ink-950); + --color-surface-subtle: var(--ink-900); + --color-surface-hover: var(--ink-850); + --color-line: var(--ink-800); + --color-line-strong: var(--ink-700); + --color-control-border: var(--ink-500); + --color-overlay: color-mix(in srgb, var(--black) 55%, transparent); + --shadow-menu: 0 10px 28px color-mix(in srgb, var(--black) 45%, transparent), 0 2px 6px color-mix(in srgb, var(--black) 30%, transparent); + + /* tier 3: header */ + --header-logo-center: var(--ink-50); + + /* tier 3: code, IntelliJ IDEA Dark (New UI); the comment is lightened from #7a7e85 (4.0:1) to 4.75:1 */ + --code-block-background: #1e1f22; + --code-block-text: #bcbec4; + --inline-code-text: #e2e2ea; + --syntax-keyword: #cf8e6d; + --syntax-string: #6aab73; + --syntax-number: #2aacb8; + --syntax-comment: #868a91; + --syntax-annotation: #b3ae60; + --syntax-function: #56a8f5; + --syntax-field: #c77dbb; + --syntax-attribute: #bababa; + --syntax-property-key: #cf8e6d; + --syntax-tag: #d5b778; + --syntax-added: #294436; + --syntax-removed: #4a2a2e; + + /* tier 3: admonitions; backgrounds are the edge hue at 9% over the surface, borders at 24%; the brand edges stay */ + --note-color: #6bdcea; + --note-background: #18272f; + --note-border-color: #1a434d; + --tip-color: #5ee0a5; + --tip-background: #182826; + --tip-border-color: #1a4536; + --warning-color: #f9c45e; + --warning-background: #2c251e; + --warning-border-color: #4e3d20; + --important-color: #ff86ab; + --important-background: #2c1723; + --important-border-color: #4e162e; + --caution-color: #b9afff; + --caution-background: #201f31; + --caution-border-color: #2f2c53; + --addon-color: #ddd8ff; + --addon-background: #201f31; + --addon-border-color: #2f2c53; + /* Space would vanish on the dark surface */ + --addon-accent: var(--jmix-violet-300); + } +} diff --git a/content/supplemental/font/LICENSE-jetbrains-mono.txt b/content/supplemental/font/LICENSE-jetbrains-mono.txt new file mode 100644 index 00000000..8f7ed671 --- /dev/null +++ b/content/supplemental/font/LICENSE-jetbrains-mono.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) JetBrainsMono-Italic[wght].ttf: Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/content/supplemental/font/LICENSE-roboto.txt b/content/supplemental/font/LICENSE-roboto.txt new file mode 100644 index 00000000..e96f4bc8 --- /dev/null +++ b/content/supplemental/font/LICENSE-roboto.txt @@ -0,0 +1,93 @@ +Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic) Roboto-Italic[wdth,wght].ttf: Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/content/supplemental/font/jetbrains-mono-cyrillic-400-italic.woff2 b/content/supplemental/font/jetbrains-mono-cyrillic-400-italic.woff2 new file mode 100644 index 00000000..ad54a035 Binary files /dev/null and b/content/supplemental/font/jetbrains-mono-cyrillic-400-italic.woff2 differ diff --git a/content/supplemental/font/jetbrains-mono-cyrillic-400-normal.woff2 b/content/supplemental/font/jetbrains-mono-cyrillic-400-normal.woff2 new file mode 100644 index 00000000..f60f0bd1 Binary files /dev/null and b/content/supplemental/font/jetbrains-mono-cyrillic-400-normal.woff2 differ diff --git a/content/supplemental/font/jetbrains-mono-latin-400-italic.woff2 b/content/supplemental/font/jetbrains-mono-latin-400-italic.woff2 new file mode 100644 index 00000000..3d3c5d77 Binary files /dev/null and b/content/supplemental/font/jetbrains-mono-latin-400-italic.woff2 differ diff --git a/content/supplemental/font/jetbrains-mono-latin-400-normal.woff2 b/content/supplemental/font/jetbrains-mono-latin-400-normal.woff2 new file mode 100644 index 00000000..58588733 Binary files /dev/null and b/content/supplemental/font/jetbrains-mono-latin-400-normal.woff2 differ diff --git a/content/supplemental/font/jetbrains-mono-latin-ext-400-italic.woff2 b/content/supplemental/font/jetbrains-mono-latin-ext-400-italic.woff2 new file mode 100644 index 00000000..c88b32dd Binary files /dev/null and b/content/supplemental/font/jetbrains-mono-latin-ext-400-italic.woff2 differ diff --git a/content/supplemental/font/jetbrains-mono-latin-ext-400-normal.woff2 b/content/supplemental/font/jetbrains-mono-latin-ext-400-normal.woff2 new file mode 100644 index 00000000..9d97c9b7 Binary files /dev/null and b/content/supplemental/font/jetbrains-mono-latin-ext-400-normal.woff2 differ diff --git a/content/supplemental/font/roboto-cyrillic-400-italic.woff2 b/content/supplemental/font/roboto-cyrillic-400-italic.woff2 new file mode 100644 index 00000000..eb5e80e1 Binary files /dev/null and b/content/supplemental/font/roboto-cyrillic-400-italic.woff2 differ diff --git a/content/supplemental/font/roboto-cyrillic-wght-normal.woff2 b/content/supplemental/font/roboto-cyrillic-wght-normal.woff2 new file mode 100644 index 00000000..9722525a Binary files /dev/null and b/content/supplemental/font/roboto-cyrillic-wght-normal.woff2 differ diff --git a/content/supplemental/font/roboto-latin-400-italic.woff2 b/content/supplemental/font/roboto-latin-400-italic.woff2 new file mode 100644 index 00000000..04367cab Binary files /dev/null and b/content/supplemental/font/roboto-latin-400-italic.woff2 differ diff --git a/content/supplemental/font/roboto-latin-ext-400-italic.woff2 b/content/supplemental/font/roboto-latin-ext-400-italic.woff2 new file mode 100644 index 00000000..ac6857f8 Binary files /dev/null and b/content/supplemental/font/roboto-latin-ext-400-italic.woff2 differ diff --git a/content/supplemental/font/roboto-latin-ext-wght-normal.woff2 b/content/supplemental/font/roboto-latin-ext-wght-normal.woff2 new file mode 100644 index 00000000..1ee37693 Binary files /dev/null and b/content/supplemental/font/roboto-latin-ext-wght-normal.woff2 differ diff --git a/content/supplemental/font/roboto-latin-wght-normal.woff2 b/content/supplemental/font/roboto-latin-wght-normal.woff2 new file mode 100644 index 00000000..9b0f1416 Binary files /dev/null and b/content/supplemental/font/roboto-latin-wght-normal.woff2 differ diff --git a/content/supplemental/img/feedback-form__thumb-up.svg b/content/supplemental/img/feedback-form__thumb-up.svg deleted file mode 100644 index b31bdb75..00000000 --- a/content/supplemental/img/feedback-form__thumb-up.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/content/supplemental/img/git-icon.svg b/content/supplemental/img/git-icon.svg deleted file mode 100644 index a3f68774..00000000 --- a/content/supplemental/img/git-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/content/supplemental/img/img/feedback-form__thumb-up.svg b/content/supplemental/img/img/feedback-form__thumb-up.svg deleted file mode 100644 index b31bdb75..00000000 --- a/content/supplemental/img/img/feedback-form__thumb-up.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/content/supplemental/img/jmix-ai-assistant-icon.svg b/content/supplemental/img/jmix-ai-assistant-icon.svg deleted file mode 100644 index d7e7b800..00000000 --- a/content/supplemental/img/jmix-ai-assistant-icon.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/content/supplemental/img/jmix-icon.svg b/content/supplemental/img/jmix-icon.svg deleted file mode 100644 index 46ea78d1..00000000 --- a/content/supplemental/img/jmix-icon.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/content/supplemental/img/warning-icon.svg b/content/supplemental/img/warning-icon.svg deleted file mode 100644 index 533e3a45..00000000 --- a/content/supplemental/img/warning-icon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/content/supplemental/js/a11y.js b/content/supplemental/js/a11y.js new file mode 100644 index 00000000..e362657e --- /dev/null +++ b/content/supplemental/js/a11y.js @@ -0,0 +1,67 @@ +// Accessibility for markup that site.js and the Lunr search UI control: aria-expanded on the toggles +// whose state site.js keeps in classes, a name for the copy buttons, focus for the skip link, and +// keyboard access to the search results. +(function () { + // site.js toggles .is-active on nav items, the explore panel and the toolbar's nav toggle (it sets the burger's aria-expanded itself) + const syncNavToggle = function (item) { + const toggle = item.querySelector(':scope > .nav-item-toggle'); + if (toggle) toggle.setAttribute('aria-expanded', String(item.classList.contains('is-active'))); + }; + const explorePanel = document.querySelector('.nav-panel-explore'); + const exploreToggle = explorePanel && explorePanel.querySelector('.context'); + const navToggle = document.querySelector('.toolbar .nav-toggle'); + const syncPanels = function () { + if (exploreToggle) exploreToggle.setAttribute('aria-expanded', String(explorePanel.classList.contains('is-active'))); + if (navToggle) navToggle.setAttribute('aria-expanded', String(navToggle.classList.contains('is-active'))); + }; + document.querySelectorAll('.nav-item').forEach(syncNavToggle); + syncPanels(); + const observer = new MutationObserver(function (records) { + records.forEach(function (record) { + if (record.target.classList.contains('nav-item')) syncNavToggle(record.target); + }); + syncPanels(); + }); + const nav = document.querySelector('.nav'); + if (nav) observer.observe(nav, { subtree: true, attributes: true, attributeFilter: ['class'] }); + if (navToggle) observer.observe(navToggle, { attributes: true, attributeFilter: ['class'] }); + + // site.js names the copy buttons through a hidden icon's alt text and an invisible toast + document.querySelectorAll('.doc .source-toolbox .copy-button').forEach(function (button) { + button.setAttribute('aria-label', 'Copy to clipboard'); + const toast = button.querySelector('.copy-toast'); + if (toast) toast.setAttribute('aria-hidden', 'true'); + }); + + // site.js intercepts in-page links to scroll them, so the skip link moves focus itself + const skipLink = document.querySelector('.skip-link'); + const main = document.getElementById('main-content'); + if (skipLink && main) { + skipLink.addEventListener('click', function () { + main.focus({ preventScroll: true }); + }); + } + + // search-ui.js runs the search 100 ms after every keydown in the input and re-renders the results; + // Escape there clears the query and the results + const searchInput = document.getElementById('search-input'); + const searchArea = searchInput && searchInput.closest('.search-field'); + if (searchArea) { + const clearSearch = function () { + searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); + }; + // keep Tab away from search-ui.js, or the result that just received focus is replaced + document.addEventListener('keydown', function (event) { + if (event.target === searchInput && event.key === 'Tab') event.stopPropagation(); + }, true); + searchArea.addEventListener('keydown', function (event) { + if (event.key === 'Escape' && event.target !== searchInput) { + searchInput.focus(); + clearSearch(); + } + }); + searchArea.addEventListener('focusout', function (event) { + if (!event.relatedTarget || !searchArea.contains(event.relatedTarget)) clearSearch(); + }); + } +})() diff --git a/content/supplemental/js/code-toolbox.js b/content/supplemental/js/code-toolbox.js new file mode 100644 index 00000000..fc4a6288 --- /dev/null +++ b/content/supplemental/js/code-toolbox.js @@ -0,0 +1,78 @@ +// Keeps code clear of the source toolbox. site.js puts the toolbox (language label and copy button) in the top +// right corner of every code block, and it is always visible, so a first line that reaches under it would be +// covered. This marks those blocks with first-line-under-toolbox, and the stylesheet starts their code below the +// toolbox. Blocks with a shorter first line are not marked. The marks follow the font loading and the width. +(function () { + const MARK = 'first-line-under-toolbox'; + // space kept between the end of the first line and the toolbox, in rem + const CLEARANCE = 0.5; + + // Right end of the first rendered line, as it is at horizontal scroll 0, or null when the block has no layout. + // A range has one rect per text fragment and per element, so the first rect is only the start of the line: + // the end is the largest right edge among the rects on the same visual line. A first line that wraps (code in + // a table cell) counts up to the end of its first visual line. + const firstLineEnd = function (code) { + const walker = document.createTreeWalker(code, NodeFilter.SHOW_TEXT); + let node = walker.nextNode(); + if (!node) return null; + const range = document.createRange(); + range.setStart(node, 0); + let last = node; + while (node && node.data.indexOf('\n') < 0) { + last = node; + node = walker.nextNode(); + } + if (node) range.setEnd(node, node.data.indexOf('\n')); + else range.setEnd(last, last.data.length); + const rects = Array.from(range.getClientRects()).filter(function (rect) { + return rect.width > 0; + }); + if (!rects.length) return null; + const line = rects.filter(function (rect) { + return rect.top < rects[0].top + rects[0].height / 2; + }); + return Math.max.apply(null, line.map(function (rect) { + return rect.right; + })) + code.scrollLeft; + }; + + const update = function () { + const clearance = CLEARANCE * parseFloat(getComputedStyle(document.documentElement).fontSize); + const blocks = []; + document.querySelectorAll('.doc .listingblock > .content').forEach(function (content) { + const toolbox = content.querySelector(':scope > .source-toolbox'); + const pre = content.querySelector(':scope > pre.highlight'); + const code = pre && pre.querySelector(':scope > code'); + if (toolbox && code) blocks.push({ pre: pre, code: code, toolbox: toolbox }); + }); + // all reads first, then all writes, so the layout is not forced once per block; only the horizontal + // direction is measured, so the top padding that a mark adds cannot change the next result; a block or a + // toolbox without layout (the toolbox is hidden in print) cannot be measured and keeps its state + const marks = blocks.map(function (block) { + const end = firstLineEnd(block.code); + const toolbox = block.toolbox.getBoundingClientRect(); + if (end === null || !toolbox.width) return block.pre.classList.contains(MARK); + return end >= toolbox.left - clearance; + }); + blocks.forEach(function (block, i) { + block.pre.classList.toggle(MARK, marks[i]); + }); + }; + + // the font decides how wide a line is, so the first measurement waits for JetBrains Mono + document.fonts.ready.then(update); + + let scheduled = false; + const schedule = function () { + if (scheduled) return; + scheduled = true; + requestAnimationFrame(function () { + scheduled = false; + update(); + }); + }; + window.addEventListener('resize', schedule); + // a block in a closed details has no layout in some engines, so opening the details measures it again (the + // toggle event does not bubble, so the listener is on the capture phase) + document.addEventListener('toggle', schedule, true); +})() diff --git a/content/supplemental/js/dropdown-menu.js b/content/supplemental/js/dropdown-menu.js index df37df2f..69a40c71 100644 --- a/content/supplemental/js/dropdown-menu.js +++ b/content/supplemental/js/dropdown-menu.js @@ -3,14 +3,24 @@ versionDropdownWrapperList.forEach(versionDropdownWrapper => { const dropdownToggle = versionDropdownWrapper.querySelector('.version-dropdown-toggle'); const dropdownMenu = versionDropdownWrapper.querySelector('.version-dropdown-menu'); - dropdownToggle.addEventListener('click', function (event) { - if (dropdownToggle.classList.contains('opened')) { - dropdownToggle.classList.remove('opened'); - dropdownMenu.classList.remove('opened'); - } else { - dropdownToggle.classList.add('opened'); - dropdownMenu.classList.add('opened'); + const setOpened = function (opened) { + dropdownToggle.classList.toggle('opened', opened); + dropdownMenu.classList.toggle('opened', opened); + dropdownToggle.setAttribute('aria-expanded', String(opened)); + }; + dropdownToggle.addEventListener('click', function () { + setOpened(!dropdownToggle.classList.contains('opened')); + }); + versionDropdownWrapper.addEventListener('keydown', function (event) { + if (event.key === 'Escape' && dropdownToggle.classList.contains('opened')) { + setOpened(false); + dropdownToggle.focus(); + } + }); + document.addEventListener('click', function (event) { + if (!versionDropdownWrapper.contains(event.target)) { + setOpened(false); } }); }); -})() \ No newline at end of file +})() diff --git a/content/supplemental/js/theme-menu.js b/content/supplemental/js/theme-menu.js new file mode 100644 index 00000000..c44d035d --- /dev/null +++ b/content/supplemental/js/theme-menu.js @@ -0,0 +1,146 @@ +// The color theme menu in the header: System, Light or Dark. The inline script in partials/head-styles.hbs sets +// data-theme and data-theme-preference on before the stylesheets load; this script keeps them up to date, +// saves the choice, follows the system setting while System is chosen, and follows choices made in other tabs. +// The menu follows the WAI-ARIA menu button pattern with menuitemradio items. +(function () { + const KEY = 'jmix-docs-theme'; + const LABELS = { system: 'System', light: 'Light', dark: 'Dark' }; + const root = document.documentElement; + const menu = document.querySelector('.theme-menu'); + if (!menu) return; + const toggle = menu.querySelector('.theme-menu-toggle'); + const list = menu.querySelector('.theme-menu-list'); + const items = Array.from(list.querySelectorAll('.theme-menu-item')); + const system = window.matchMedia('(prefers-color-scheme: dark)'); + + // the stored preference, or null when storage is blocked + const read = function () { + try { + const value = localStorage.getItem(KEY); + return value === 'light' || value === 'dark' ? value : 'system'; + } catch (e) { + return null; + } + }; + const save = function (preference) { + try { + if (preference === 'system') localStorage.removeItem(KEY); + else localStorage.setItem(KEY, preference); + } catch (e) { + // storage is blocked: the choice holds for this page only + } + }; + + let preference = LABELS[root.getAttribute('data-theme-preference')] ? root.getAttribute('data-theme-preference') : read() || 'system'; + + // every path goes through here: a choice, the system setting, another tab, a page restored from the cache + const apply = function (next) { + preference = next; + const dark = next === 'dark' || (next === 'system' && system.matches); + root.setAttribute('data-theme', dark ? 'dark' : 'light'); + root.setAttribute('data-theme-preference', next); + const meta = document.querySelector('meta[name="color-scheme"]'); + if (meta) meta.content = dark ? 'dark' : 'light'; + const label = 'Color theme: ' + LABELS[next]; + toggle.setAttribute('aria-label', label); + toggle.setAttribute('title', label); + items.forEach(function (item) { + item.setAttribute('aria-checked', String(item.dataset.themeOption === next)); + }); + }; + + const isOpen = function () { + return list.classList.contains('opened'); + }; + const open = function () { + list.classList.add('opened'); + toggle.setAttribute('aria-expanded', 'true'); + const checked = items.find(function (item) { + return item.dataset.themeOption === preference; + }); + (checked || items[0]).focus(); + }; + const close = function (returnFocus) { + list.classList.remove('opened'); + toggle.setAttribute('aria-expanded', 'false'); + if (returnFocus) toggle.focus(); + }; + + // Alt, Ctrl and Meta shortcuts are the browser's. Shift is not one of them, since Shift+Tab leaves the menu. + const hasModifier = function (event) { + return event.altKey || event.ctrlKey || event.metaKey; + }; + + // Enter and Space reach the buttons as clicks + toggle.addEventListener('click', function () { + if (isOpen()) close(false); + else open(); + }); + toggle.addEventListener('keydown', function (event) { + if (hasModifier(event)) return; + if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { + event.preventDefault(); + open(); + } + }); + items.forEach(function (item) { + item.addEventListener('click', function () { + apply(item.dataset.themeOption); + save(preference); + close(true); + }); + }); + // a press on the panel's padding would move focus to the page, where the arrows and Escape do nothing + list.addEventListener('mousedown', function (event) { + if (!event.target.closest('.theme-menu-item')) event.preventDefault(); + }); + list.addEventListener('keydown', function (event) { + if (hasModifier(event)) return; + // Tab and Shift+Tab close the menu; focus then moves on from the hidden item. Shift+Tab lands on the button, + // which is inside the menu element, so the focusin listener below would not close the menu. + if (event.key === 'Tab') { + close(false); + return; + } + const index = items.indexOf(document.activeElement); + const target = { + ArrowDown: (index + 1) % items.length, + ArrowUp: (index - 1 + items.length) % items.length, + Home: 0, + End: items.length - 1, + }[event.key]; + if (target !== undefined) { + event.preventDefault(); + items[target].focus(); + } + }); + menu.addEventListener('keydown', function (event) { + if (event.key === 'Escape' && isOpen()) { + event.preventDefault(); + close(true); + } + }); + // focus moving elsewhere (the / shortcut) or a click elsewhere closes the menu. Not focusout: Safari does + // not focus a clicked button, so focus leaves the menu on mousedown and the click would never reach the item. + document.addEventListener('focusin', function (event) { + if (isOpen() && !menu.contains(event.target)) close(false); + }); + // capture phase: site.js stops clicks in the navigation, on the burger and on the toolbar's nav toggle from bubbling + document.addEventListener('click', function (event) { + if (isOpen() && !menu.contains(event.target)) close(false); + }, true); + + system.addEventListener('change', function () { + if (preference === 'system') apply('system'); + }); + // other tabs write the key; this tab does not get its own storage events. key null means localStorage.clear(). + window.addEventListener('storage', function (event) { + if (event.key === KEY || event.key === null) apply(read() || preference); + }); + // a page restored from the back/forward cache missed the storage events of the meantime + window.addEventListener('pageshow', function (event) { + if (event.persisted) apply(read() || preference); + }); + + apply(preference); +})(); diff --git a/content/supplemental/partials/footer-content.hbs b/content/supplemental/partials/footer-content.hbs new file mode 100644 index 00000000..2faa5135 --- /dev/null +++ b/content/supplemental/partials/footer-content.hbs @@ -0,0 +1,7 @@ +{{! This Source Code Form is subject to the terms of the Mozilla Public }} +{{! License, v. 2.0. If a copy of the MPL was not distributed with this }} +{{! file, You can obtain one at http://mozilla.org/MPL/2.0/. }} +
+

This page was built with Antora. Its UI is based on the Antora default UI.

+

The source code for this UI is licensed under the terms of the MPL-2.0 license.

+
diff --git a/content/supplemental/partials/footer-scripts.hbs b/content/supplemental/partials/footer-scripts.hbs index 8f4e9ece..5d5d4c0d 100644 --- a/content/supplemental/partials/footer-scripts.hbs +++ b/content/supplemental/partials/footer-scripts.hbs @@ -9,23 +9,24 @@ hljs.highlightAll(); - + + + + - diff --git a/content/supplemental/partials/head-meta.hbs b/content/supplemental/partials/head-meta.hbs index 1ff801af..284cfbc9 100644 --- a/content/supplemental/partials/head-meta.hbs +++ b/content/supplemental/partials/head-meta.hbs @@ -1,9 +1,5 @@ {{! This Source Code Form is subject to the terms of the Mozilla Public }} {{! License, v. 2.0. If a copy of the MPL was not distributed with this }} {{! file, You can obtain one at http://mozilla.org/MPL/2.0/. }} - - - - - \ No newline at end of file + diff --git a/content/supplemental/partials/head-scripts.hbs b/content/supplemental/partials/head-scripts.hbs index 712da167..1e8bfaf3 100644 --- a/content/supplemental/partials/head-scripts.hbs +++ b/content/supplemental/partials/head-scripts.hbs @@ -1,3 +1,6 @@ +{{! This Source Code Form is subject to the terms of the Mozilla Public }} +{{! License, v. 2.0. If a copy of the MPL was not distributed with this }} +{{! file, You can obtain one at http://mozilla.org/MPL/2.0/. }} + + + + + diff --git a/content/supplemental/partials/header-content.hbs b/content/supplemental/partials/header-content.hbs index e9d56b43..29677478 100644 --- a/content/supplemental/partials/header-content.hbs +++ b/content/supplemental/partials/header-content.hbs @@ -1,28 +1,19 @@ {{! This Source Code Form is subject to the terms of the Mozilla Public }} {{! License, v. 2.0. If a copy of the MPL was not distributed with this }} {{! file, You can obtain one at http://mozilla.org/MPL/2.0/. }} +
diff --git a/content/supplemental/partials/main.hbs b/content/supplemental/partials/main.hbs new file mode 100644 index 00000000..f5536519 --- /dev/null +++ b/content/supplemental/partials/main.hbs @@ -0,0 +1,14 @@ +{{! This Source Code Form is subject to the terms of the Mozilla Public }} +{{! License, v. 2.0. If a copy of the MPL was not distributed with this }} +{{! file, You can obtain one at http://mozilla.org/MPL/2.0/. }} +
+{{> toolbar}} +
+{{#if (eq page.layout '404')}} +{{> article-404}} +{{else}} +{{> toc}} +{{> article}} +{{/if}} +
+
diff --git a/content/supplemental/partials/nav-explore.hbs b/content/supplemental/partials/nav-explore.hbs index ed0f194f..1c3f92d5 100644 --- a/content/supplemental/partials/nav-explore.hbs +++ b/content/supplemental/partials/nav-explore.hbs @@ -1,9 +1,12 @@ +{{! This Source Code Form is subject to the terms of the Mozilla Public }} +{{! License, v. 2.0. If a copy of the MPL was not distributed with this }} +{{! file, You can obtain one at http://mozilla.org/MPL/2.0/. }}