diff --git a/README.md b/README.md index 6c7dbfb75f..304efbf8f1 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,23 @@ Nav hrefs are root-relative and prefixed with `params.ttnOrigin` (default `https://www.thethingsnetwork.org`); for a local all-in-one preview run Hugo with `HUGO_PARAMS_TTNORIGIN=http://localhost:3000`. -Section landing pages can use the `lead`, `actions`/`button`, `chapters`, `feature` and `topics` -shortcodes (see `doc/content/lorawan/_index.md`); a section's numbered course is its `chapters` -front matter and its topic index the `topics` front matter. +Fonts, brand tokens and the page header (`tn-ph`) come from `doc/assets/css/ttn-tokens.css` and +`doc/static/fonts/ttn/`, also **generated** by the platform repo (contract: its +`docs/brand-page-header.md`). Re-export them after a token, font or page-header change there: + +``` +node scripts/export-brand-tokens.mjs /doc/assets/css/ttn-tokens.css /doc/static/fonts/ttn ../fonts/ttn +``` + +`doc/assets/css/docs.css` holds only the docs' own components; it uses the shared tokens and keeps +the header/footer rules identical to the website's `site.css`. + +Every page with the left navigation opens with the page header (`layouts/partials/docs/page-header.html`): +breadcrumb, an eyebrow (the page's sidebar group), the title, a lede (the `lead` front matter, else +`description`) and buttons (`actions` front matter: `label`, `ref` or `href`, `style`, `icon`, +`icon_before`). Section landing pages can also use the `chapters`, `feature` and `topics` shortcodes +(see `doc/content/lorawan/_index.md`); a section's numbered course is its `chapters` front matter and +its topic index the `topics` front matter. ## Getting Started diff --git a/doc/assets/css/docs.css b/doc/assets/css/docs.css index 1de63f5ca1..f28b9cffc6 100644 --- a/doc/assets/css/docs.css +++ b/doc/assets/css/docs.css @@ -1,34 +1,17 @@ -/* The Things Network docs theme (2026 redesign). - Tokens: The Things Industries design system (colors_and_type.css), TTN brand. - Chrome (header/footer) mirrors the www platform's site.css so /docs looks like the rest of the site. */ - -/* ---------- Fonts ---------- */ -@font-face { font-family: "Inter"; font-weight: 400; font-style: normal; font-display: swap; src: url("../fonts/inter/Inter-Regular.woff2") format("woff2"); } -@font-face { font-family: "Inter"; font-weight: 500; font-style: normal; font-display: swap; src: url("../fonts/inter/Inter-Medium.woff2") format("woff2"); } -@font-face { font-family: "Inter"; font-weight: 600; font-style: normal; font-display: swap; src: url("../fonts/inter/Inter-SemiBold.woff2") format("woff2"); } -@font-face { font-family: "Inter"; font-weight: 700; font-style: normal; font-display: swap; src: url("../fonts/inter/Inter-Bold.woff2") format("woff2"); } -@font-face { font-family: "Poppins"; font-weight: 700; font-style: normal; font-display: swap; src: url("https://fonts.gstatic.com/s/poppins/v22/pxiByp8kv8JHgFVrLCz7Z1xlFd2JQEk.woff2") format("woff2"); } - -/* ---------- Tokens ---------- */ +/* The Things Network docs theme (2026 redesign, brand harmonization 2026-10-14). + Loaded AFTER ttn-tokens.css (assets/css/, GENERATED by thethingsnetwork-v2 + platform/scripts/export-brand-tokens.mjs; never edit it here): that file brings the fonts (Inter + 300–600, Poppins SemiBold 600, self-hosted), every brand token (--grey-*, --tti-*, --ttn-*, --tn-*, + --link*, --type-*, radius, shadow, motion), .tn-wrap, .tn-eyebrow, the shared motion rules and the + page header (tn-ph, contract: thethingsnetwork-v2 docs/brand-page-header.md). + This file holds only the docs' own components, plus the header/footer chrome, which mirrors the + website's platform/app/styles/site.css (Header, Footer, Buttons) rule for rule: keep them identical. */ + +/* ---------- Docs-only tokens ---------- */ :root { - --grey-025: #fbfcfd; --grey-050: #f3f5f8; --grey-075: #ebeef3; --grey-100: #dee3ea; --grey-200: #d2d7e0; - --grey-300: #c3c9d4; --grey-400: #b2bac8; --grey-500: #98a4b6; --grey-600: #7887a1; --grey-700: #536482; - --grey-800: #354661; --grey-900: #1c293f; --grey-1000: #0c1525; - - --info-050: #effaff; --info-100: #ccefff; --info-600: #005edb; - --warning-050: #fff9e6; --warning-100: #ffeebc; --warning-600: #db7600; --warning-700: #b75b00; - - --ttn: #26a0ff; --ttn-600: #008df9; --ttn-700: #0071c7; - --navy: #001859; --navy-900: #00103f; --ink: #000e36; - --tint: #e6f4ff; --muted-invert: #aebbd3; --navy-text: #c7d4ea; - - --font-display: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; - --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; - --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; - - --br-s: 4px; --br-m: 9px; --br-l: 0.9rem; --br-xl: 1.25rem; --br-pill: 3rem; - --bs-s: 0 0.2rem 1rem 0 rgb(0 0 0 / 5%); - --header-h: 72px; /* = the global header on www (platform site.css .tn-header__bar) */ + --navy-text: #c7d4ea; /* body text on the navy feature panel */ + --dx-br-code: 4px; /* inline code chips only */ + --header-h: 72px; /* = the global header on www (site.css .tn-header__bar) */ } /* ---------- Base ---------- */ @@ -38,119 +21,156 @@ body { margin: 0; font-family: var(--font-body); font-size: 1rem; - line-height: 1.6; + line-height: 1.4; /* Body / Default 15/21 */ color: var(--grey-900); background: #fff; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -a { color: var(--ttn-600); text-decoration: none; } -a:hover { color: var(--navy); } +/* Text links: tti-500 on light surfaces, tti-300 on dark ones (colors.md "Usage rules"). Navigation, + buttons and cards set their own colour below. */ +a { color: var(--link); text-decoration: none; } +a:hover { color: var(--link-hover); } +b, strong { font-weight: 600; } img { max-width: 100%; height: auto; } -:focus-visible { outline: 3px solid #cce8ff; outline-offset: 2px; border-radius: 4px; } +:focus-visible { outline: 2px solid var(--tti-500, #0047FF); outline-offset: 2px; border-radius: 4px; } + +/* The docs' own interactive parts join the shared state-change transition (ttn-tokens.css §7 covers + a, button, summary): color, background, border and shadow only, never `all` or layout properties. */ +.dx-search__field, .dx-search__item, .dx-anchor, .dx-chapter__arrow, .dx-card__arrow, +.dx-prose tbody tr { + transition-property: color, background-color, border-color, box-shadow; + transition-duration: var(--dur-fast); + transition-timing-function: var(--ease-standard); +} +/* Icons: Tabler, 1em of the parent unless set; the docs' own icons sit on 16 or 24 (icons.md). */ .dx-icon { width: 1em; height: 1em; flex: none; vertical-align: -0.125em; } .dx-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; } -.dx-skip { position: absolute; left: 16px; top: -48px; z-index: 50; padding: 8px 14px; border-radius: var(--br-m); background: var(--navy); color: #fff; } +.dx-skip { position: absolute; left: 16px; top: -48px; z-index: 50; padding: 8px 14px; border-radius: var(--br-m); background: var(--tn-navy); color: #fff; } .dx-skip:focus { top: 12px; color: #fff; } -.dx-wrap { max-width: 1200px; margin-inline: auto; padding-inline: 24px; } -/* ---------- Buttons ---------- */ +/* ---------- Buttons (= site.css .tn-btn) ---------- */ .dx-btn { display: inline-flex; align-items: center; gap: 8px; - padding: 12px 22px; border: 1px solid transparent; border-radius: var(--br-pill); + padding: 12px 22px; border: 1px solid transparent; border-radius: var(--br-m); font: inherit; font-size: 15px; font-weight: 600; line-height: 1.2; white-space: nowrap; - cursor: pointer; transition: background-color 0.1s, border-color 0.1s, color 0.1s; + cursor: pointer; } +.dx-btn .dx-icon { font-size: 16px; } .dx-btn p { margin: 0; } .dx-btn--sm { padding: 9px 18px; font-size: 14px; } -.dx-btn, .dx-btn--brand { background: var(--ttn); color: #fff; } -.dx-btn:hover, .dx-btn--brand:hover { background: var(--ttn-600); color: #fff; } -.dx-btn--primary { background: var(--navy); color: #fff; } -.dx-btn--primary:hover { background: var(--navy-900); color: #fff; } -.dx-btn--outline { background: transparent; border-color: var(--grey-300); color: var(--navy); } -.dx-btn--outline:hover { background: transparent; border-color: var(--grey-500); color: var(--navy); } -.dx-actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 24px 0; } - -/* ---------- Header ---------- */ +.dx-btn, .dx-btn--brand { background: var(--tn-blue); color: #fff; } +.dx-btn:hover, .dx-btn--brand:hover { background: var(--tn-blue-600); color: #fff; } +.dx-btn--primary { background: var(--tn-navy); color: #fff; } +.dx-btn--primary:hover { background: var(--tn-navy-900); color: #fff; } +.dx-btn--outline { background: transparent; border-color: var(--grey-300); color: var(--tn-navy); } +.dx-btn--outline:hover { background: transparent; border-color: var(--grey-500); color: var(--tn-navy); } +/* Page header actions: the shared button spec (brand-page-header.md, tn-ph__actions = the website's + .tn-btn--lg): 48px, 14px 26px, primary = the ttn-500 fill, secondary = white with a grey-200 border. */ +.tn-ph__actions .dx-btn { padding: 14px 26px; } +.tn-ph__actions .dx-btn--primary { background: var(--tn-blue); } +.tn-ph__actions .dx-btn--primary:hover { background: var(--tn-blue-600); } +.tn-ph__actions .dx-btn--outline { background: #fff; border-color: var(--grey-200); } +.tn-ph__actions .dx-btn--outline:hover { background: #fff; border-color: var(--grey-400); } +.dx-btn:focus-visible, +.dx-header__link:focus-visible, +.dx-header__toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); } + +/* ---------- Header (= site.css Header; docs-only: the search box) ---------- */ .dx-header { position: sticky; top: 0; z-index: 20; background: rgb(255 255 255 / 88%); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); border-bottom: 1px solid var(--grey-100); + font-family: var(--font-body); } .dx-header__bar { max-width: 1200px; height: var(--header-h); margin-inline: auto; padding-inline: 24px; display: flex; align-items: center; gap: 32px; } .dx-header__logo { flex: none; } -.dx-header__logo img { display: block; height: 34px; width: auto; } +.dx-header__logo img { display: block; height: 28px; width: auto; } /* brand lockup 150×28 at its native size */ .dx-header__panel { flex: 1; min-width: 0; display: flex; align-items: center; gap: 16px; } .dx-header__nav { display: flex; align-items: center; gap: 2px; } .dx-header__link { display: inline-flex; align-items: center; gap: 4px; - padding: 6px 10px; border-radius: var(--br-m); - color: var(--grey-800); font-size: 14px; font-weight: 500; line-height: 24px; white-space: nowrap; - cursor: pointer; list-style: none; + padding: 6px 10px; border: 0; border-radius: var(--br-m); background: none; + color: var(--grey-800); font: inherit; font-size: 14px; font-weight: 500; white-space: nowrap; + cursor: pointer; line-height: 1.2; list-style: none; } .dx-header__link::-webkit-details-marker { display: none; } -.dx-header__link:hover { background: var(--grey-050); color: var(--grey-900); } -.dx-header__link[aria-current="page"] { color: var(--navy); font-weight: 600; } /* no pill: same as the global header */ -.dx-header__link--highlight { background: var(--tint); color: var(--ttn-700); font-weight: 600; } /* same as the global header */ -.dx-header__link--highlight:hover { background: #cce8ff; color: var(--ttn-700); } -.dx-header__cta { display: inline-flex; align-items: center; justify-content: center; padding: 8px 14px; border-radius: var(--br-pill); background: var(--navy); color: #fff; font-size: 14px; font-weight: 600; white-space: nowrap; } -.dx-header__cta:hover { background: var(--navy-900); color: #fff; } +.dx-header__link:hover, .dx-drop[open] > .dx-header__link { background: var(--grey-050); color: var(--grey-900); } +.dx-header__link[aria-current="page"] { color: var(--tn-navy); font-weight: 600; } +.dx-header__link--highlight, .dx-header__link--highlight:hover { background: var(--tn-tint); color: var(--ttn-700); font-weight: 600; } /* 5.7:1 / 5.1:1 on the hover fill */ +.dx-header__link--highlight:hover { background: #cce8ff; } +.dx-header__cta { display: inline-flex; align-items: center; justify-content: center; padding: 8px 14px; border-radius: var(--br-m); background: var(--tn-navy); color: #fff; font-size: 14px; font-weight: 600; white-space: nowrap; line-height: 1.2; } +.dx-header__cta:hover { background: var(--tn-navy-900); color: #fff; } .dx-header__tools { flex: 1; display: flex; justify-content: flex-end; align-items: center; gap: 8px; } -.dx-header__auth { display: flex; align-items: center; gap: 4px; } -.dx-header__login { padding: 8px 14px; color: var(--grey-800); font-size: 14px; font-weight: 500; white-space: nowrap; } -.dx-header__login:hover { color: var(--navy); } -.dx-header__user { display: inline-flex; align-items: center; gap: 8px; padding: 4px 10px 4px 4px; border-radius: var(--br-pill); color: var(--grey-900); font-size: 14px; font-weight: 600; } -.dx-header__user:hover { background: var(--grey-050); color: var(--grey-900); } +.dx-header__auth { display: flex; align-items: center; gap: 8px; } +.dx-header__login { padding: 8px 14px; color: var(--grey-800); font-size: 14px; font-weight: 500; line-height: 1.2; white-space: nowrap; } /* nowrap: the docs bar also holds the search */ +.dx-header__login:hover { color: var(--grey-900); } +/* The signed-in member (= .tn-header__account), rendered by docs.js or www's /site-session.js. */ +.dx-header__user { + display: inline-flex; align-items: center; gap: 8px; + padding: 4px 8px 4px 4px; border: 1px solid var(--grey-100); border-radius: var(--br-m); background: #fff; + color: var(--grey-900); font: inherit; font-size: 14px; font-weight: 500; cursor: pointer; line-height: 1.2; +} +.dx-header__user:hover { color: var(--grey-900); } .dx-header__user img { width: 28px; height: 28px; border-radius: 50%; background: var(--grey-050); } -.dx-header__toggle { display: none; margin-left: auto; padding: 8px; border: 0; border-radius: var(--br-m); background: transparent; color: var(--grey-900); font-size: 22px; line-height: 0; cursor: pointer; } +.dx-header__toggle { display: none; margin-left: auto; padding: 8px; border: 0; border-radius: var(--br-m); background: none; color: var(--grey-900); font-size: 24px; line-height: 0; cursor: pointer; } .dx-header__toggle-close { display: none; } .dx-drop { position: relative; } -.dx-drop__chev { font-size: 14px; transition: transform 0.15s; } +.dx-drop__chev { font-size: 14px; transition: transform var(--dur-base) var(--ease-standard); } .dx-drop[open] .dx-drop__chev { transform: rotate(180deg); } +/* Opens with the website's fade + 4px drop (§12);
closes instantly. */ .dx-drop__menu { - position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; min-width: 200px; + position: absolute; top: calc(100% + 8px); left: 0; z-index: 30; min-width: 220px; display: flex; flex-direction: column; padding: 6px; - background: #fff; border: 1px solid var(--grey-100); border-radius: var(--br-l); box-shadow: 0 12px 32px rgb(0 24 89 / 12%); + background: #fff; border: 1px solid var(--grey-100); border-radius: var(--br-l); box-shadow: var(--bs-m); + transition: opacity var(--dur-base) var(--ease-standard), transform var(--dur-base) var(--ease-standard); +} +@starting-style { + .dx-drop[open] .dx-drop__menu { opacity: 0; transform: translateY(-4px); } +} +.dx-drop__item { + display: flex; align-items: center; gap: 10px; width: 100%; + padding: 9px 12px; border: 0; border-radius: var(--br-m); background: none; + color: var(--grey-900); font: inherit; font-size: 14px; font-weight: 500; text-align: left; cursor: pointer; line-height: 1.2; + white-space: nowrap; } -.dx-drop__item { padding: 8px 12px; border-radius: var(--br-m); color: var(--grey-800); font-size: 14px; white-space: nowrap; } .dx-drop__item:hover { background: var(--grey-050); color: var(--grey-900); } /* Signed-in account menu, rendered by www's /site-session.js into [data-ttn-auth="dx"]. */ -summary.dx-header__user { list-style: none; cursor: pointer; } +summary.dx-header__user { list-style: none; } summary.dx-header__user::-webkit-details-marker { display: none; } .dx-drop--end > .dx-drop__menu { left: auto; right: 0; } -.dx-drop--end .dx-drop__item { display: flex; align-items: center; gap: 8px; } .dx-drop--end form { margin: 0; } -.dx-drop--end button.dx-drop__item { width: 100%; border: 0; background: none; font: inherit; font-size: 14px; text-align: left; cursor: pointer; } -/* Search */ +/* Search (docs only) */ .dx-search { position: relative; width: min(100%, 240px); } .dx-search__field { - display: flex; align-items: center; gap: 8px; height: 38px; padding: 0 12px; - border: 1px solid var(--grey-200); border-radius: var(--br-m); background: var(--grey-025); color: var(--grey-600); - font-size: 17px; + display: flex; align-items: center; gap: 8px; height: 40px; padding: 0 12px; + border: 1px solid var(--grey-200); border-radius: var(--br-m); background: var(--grey-025); color: var(--grey-700); + font-size: 16px; } -.dx-search__field:focus-within { border-color: var(--ttn); background: #fff; box-shadow: 0 0 0 3px #cce8ff; } +.dx-search__field:focus-within { border-color: var(--tn-blue); background: #fff; box-shadow: var(--focus-ring); } .dx-search__field input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; font: inherit; font-size: 14px; color: var(--grey-900); } +.dx-search__field input::placeholder { color: var(--grey-700); } .dx-search__field input::-webkit-search-cancel-button { display: none; } -.dx-search__field kbd { font-family: var(--font-mono); font-size: 11px; line-height: 1.4; padding: 1px 6px; border: 1px solid var(--grey-200); border-radius: var(--br-s); background: #fff; color: var(--grey-600); } +.dx-search__field kbd { font-family: var(--font-mono); font-size: 11px; line-height: 1.4; padding: 1px 6px; border: 1px solid var(--grey-200); border-radius: var(--dx-br-code); background: #fff; color: var(--grey-700); } .dx-search__field:focus-within kbd { display: none; } .dx-search__results { - position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; width: min(440px, calc(100vw - 32px)); max-height: min(70vh, 480px); overflow-y: auto; - padding: 6px; background: #fff; border: 1px solid var(--grey-100); border-radius: var(--br-l); box-shadow: 0 12px 32px rgb(0 24 89 / 14%); + position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: min(440px, calc(100vw - 32px)); max-height: min(70vh, 480px); overflow-y: auto; + padding: 6px; background: #fff; border: 1px solid var(--grey-100); border-radius: var(--br-l); box-shadow: var(--bs-m); } .dx-search__item { display: block; padding: 10px 12px; border-radius: var(--br-m); color: var(--grey-900); } -.dx-search__item:hover, .dx-search__item[aria-selected="true"] { background: var(--tint); color: var(--grey-900); } -.dx-search__title { display: block; font-size: 14px; font-weight: 600; color: var(--navy); } -.dx-search__meta { display: block; margin-top: 2px; font-size: 12.5px; line-height: 1.45; color: var(--grey-600); } -.dx-search__empty { padding: 12px; font-size: 14px; color: var(--grey-600); } -.dx-search__item mark { background: transparent; color: var(--ttn-700); font-weight: 700; } +.dx-search__item:hover, .dx-search__item[aria-selected="true"] { background: var(--tn-tint); color: var(--grey-900); } +.dx-search__title { display: block; font-size: 14px; font-weight: 600; line-height: 17px; color: var(--tn-navy); } +.dx-search__meta { display: block; margin-top: 2px; font-size: 13px; line-height: 20px; color: var(--grey-700); } /* Body / XS */ +.dx-search__empty { padding: 12px; font-size: 14px; color: var(--grey-700); } +.dx-search__item mark { background: transparent; color: var(--tn-blue-700); font-weight: 600; } /* Desktop: the header also carries the support button, so search rests as an icon and opens into the full field over its neighbours on click or "/" (the field is a