diff --git a/dictionary-octopus.txt b/dictionary-octopus.txt index cdcca35cbf..a94359012f 100644 --- a/dictionary-octopus.txt +++ b/dictionary-octopus.txt @@ -337,7 +337,6 @@ netstandard newcert newguid NEWID -newnav Newtonsoft nfsadmin nlog diff --git a/src/components/Header.astro b/src/components/Header.astro deleted file mode 100644 index 834d8fc676..0000000000 --- a/src/components/Header.astro +++ /dev/null @@ -1,81 +0,0 @@ ---- -import { accelerator } from '@lib/accelerator'; -import type { Frontmatter } from 'astro-accelerator-utils/types/Frontmatter'; -import { Lang } from '@util/Languages'; -import { SITE } from '@config'; -import Search from '../themes/octopus/components/Search.astro'; -import MobileMenu from './MobileMenu.astro'; -import OctopusLogo from './icons/OctopusLogo.astro'; -import ThemeSwitcher from './ThemeSwitcher.astro'; -import TopNav from './TopNav.astro'; - -const stats = new accelerator.statistics('components/Header.astro'); -stats.start(); - -// Properties -type Props = { - lang: string; - frontmatter: Frontmatter; - headings: { depth: number; slug: string; text: string }[]; - showSearch: boolean; -}; -const { lang, showSearch } = Astro.props satisfies Props; - -// Language -const _ = Lang(lang); - -stats.stop(); ---- - - - -
- -
- - diff --git a/src/components/MobileMenu.astro b/src/components/MobileMenu.astro deleted file mode 100644 index b01e906f37..0000000000 --- a/src/components/MobileMenu.astro +++ /dev/null @@ -1,26 +0,0 @@ ---- -// Drawer body is populated client-side by nav-mobile.js cloning #site-nav. -import ThemeSwitcher from './ThemeSwitcher.astro'; ---- - -
- - - - - - - -
diff --git a/src/components/ThemeSwitcher.astro b/src/components/ThemeSwitcher.astro deleted file mode 100644 index 8967e152f5..0000000000 --- a/src/components/ThemeSwitcher.astro +++ /dev/null @@ -1,44 +0,0 @@ ---- -import SunIcon from './icons/SunIcon.astro'; -import MoonIcon from './icons/MoonIcon.astro'; - -type Props = { - /** Unique per instance - the label needs its own `for` target. */ - id: string; -}; - -const { id } = Astro.props satisfies Props; ---- - -
- - -
- - diff --git a/src/components/TopNav.astro b/src/components/TopNav.astro index 6b0e6b55d4..cdbc0cb022 100644 --- a/src/components/TopNav.astro +++ b/src/components/TopNav.astro @@ -170,6 +170,10 @@ const sectionItems: MenuItem[] = links.map(({ label, href }) => ({ ); box-sizing: border-box; + position: fixed; + inset-block-start: 0; + inset-inline: 0; + z-index: 11; display: grid; grid-template-columns: minmax(max-content, var(--top-nav-content-start)) diff --git a/src/components/icons/MoonIcon.astro b/src/components/icons/MoonIcon.astro deleted file mode 100644 index 45d26fef9d..0000000000 --- a/src/components/icons/MoonIcon.astro +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/components/icons/OctopusLogo.astro b/src/components/icons/OctopusLogo.astro deleted file mode 100644 index c31fb459e1..0000000000 --- a/src/components/icons/OctopusLogo.astro +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file diff --git a/src/components/icons/SunIcon.astro b/src/components/icons/SunIcon.astro deleted file mode 100644 index 612cbda578..0000000000 --- a/src/components/icons/SunIcon.astro +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/layouts/Api.astro b/src/layouts/Api.astro index a62b77d0e8..5b09cd7d7b 100644 --- a/src/layouts/Api.astro +++ b/src/layouts/Api.astro @@ -17,7 +17,7 @@ import Taxonomy from '@components/Taxonomy.astro'; import ApiNavigation from '../components/ApiNavigation.astro'; import ArticleHeader from '../components/ArticleHeader.astro'; import Feedback from '../components/Feedback.astro'; -import Header from '../components/Header.astro'; +import TopNav from '../components/TopNav.astro'; import Plausible from 'src/components/Plausible.astro'; import Footer from 'src/components/Footer.astro'; import DocsSearch from '../components/DocsSearch.astro'; @@ -72,12 +72,7 @@ const lastUpdated = frontmatter.modDate ?? frontmatter.pubDate ?? null; /> -
+ diff --git a/src/layouts/Default.astro b/src/layouts/Default.astro index 9f0fd7b565..d6f591caf0 100644 --- a/src/layouts/Default.astro +++ b/src/layouts/Default.astro @@ -17,7 +17,7 @@ import Taxonomy from '@components/Taxonomy.astro'; import ArticleHeader from '../components/ArticleHeader.astro'; import ArticleNav from '../components/ArticleNav.astro'; import Feedback from '../components/Feedback.astro'; -import Header from '../components/Header.astro'; +import TopNav from '../components/TopNav.astro'; import EditOnGithub from '../components/EditOnGithub.astro'; import CopyAsMarkdown from '../components/CopyAsMarkdown.astro'; import MarkdownLinks from '../components/MarkdownLinks.astro'; @@ -70,12 +70,7 @@ const lastUpdated = frontmatter.modDate ?? frontmatter.pubDate ?? null; /> -
+
diff --git a/src/scripts/main.js b/src/scripts/main.js index 90584d1510..3fa6d85c28 100644 --- a/src/scripts/main.js +++ b/src/scripts/main.js @@ -4,8 +4,6 @@ import { addListImageIntersectionObserver, } from './modules/animation.js'; import { addResizedEvent } from './modules/resizing.js'; -import { addStickyNavigation } from './modules/nav-sticky.js'; -import { mobileNav } from './modules/nav-mobile.js'; import { markdownLinkMenus } from './modules/markdown-links.js'; import { setClickableBlocks } from './modules/click-blocks.js'; import { setExternalLinkAttributes } from './modules/external-links.js'; @@ -14,17 +12,11 @@ import { enableSharing } from './modules/share.js'; import { highlightCurrentHeading } from './modules/toc.js'; import { enhanceTooltips } from './modules/tooltips'; -const resizedEventName = addResizedEvent(); +addResizedEvent(); enhanceTooltips(); setClickableBlocks(); setExternalLinkAttributes(); -addStickyNavigation( - '.site-header', - '#site-nav', - '#site-nav > ul', - resizedEventName -); addIntersectionObserver('.anim-show-parent img, .anim-show-parent .card'); addListImageIntersectionObserver('.card img'); monitorInputType(); diff --git a/src/scripts/modules/nav-mobile.js b/src/scripts/modules/nav-mobile.js deleted file mode 100644 index 075a521410..0000000000 --- a/src/scripts/modules/nav-mobile.js +++ /dev/null @@ -1,100 +0,0 @@ -// @ts-check -import { qs, qsa } from './query.js'; -import { removeScroll, resetScroll } from './scrollbar.js'; - -class MobileNav { - constructor() { - this.mobileMenuWrapper = qs('[data-mobile-menu-wrapper]'); - this.hamburgerIcon = qs('[data-hamburger-icon]'); - this.mobileMenu = qs('[data-mobile-menu]'); - this.mobileMenuList = qs('[data-mobile-menu-list]'); - - this.populateMobileMenu(); - this.menuItems = qsa('[data-mobile-menu-list] li'); - - // Initially hide the menu - this.mobileMenu.style.visibility = 'hidden'; - - this.addListeners(); - } - - populateMobileMenu() { - // Idempotent on hot-reload. - if (this.mobileMenuList.children.length > 0) return; - - const sourceList = document.querySelector('#site-nav .site-nav__list'); - if (!sourceList) { - console.warn( - '[nav-mobile] #site-nav not found; mobile drawer will be empty' - ); - return; - } - - for (const child of sourceList.children) { - this.mobileMenuList.appendChild(child.cloneNode(true)); - } - } - - toggleMobileMenu() { - const isOpen = this.mobileMenuWrapper.classList.contains('is-active'); - if (isOpen) { - this.closeMobileMenu(); - } else { - this.openMobileMenu(); - } - } - - openMobileMenu() { - this.mobileMenu.style.visibility = 'visible'; - this.mobileMenuWrapper.classList.add('is-active'); - this.hamburgerIcon.setAttribute('aria-expanded', 'true'); - - removeScroll(); - } - - closeMobileMenu() { - this.mobileMenuWrapper.classList.remove('is-active'); - this.hamburgerIcon.setAttribute('aria-expanded', 'false'); - - // Wait for the transition to complete before hiding the menu - setTimeout(() => { - this.mobileMenu.style.visibility = 'hidden'; - }, 500); - - resetScroll(); - } - - handleDropdownKeyboardNavigation(e) { - if (!this.mobileMenuWrapper.classList.contains('is-active')) return; - if (e.key === 'Escape') { - this.closeMobileMenu(); - } - - if (e.key === 'Tab') { - const firstElement = this.hamburgerIcon; - const lastElement = this.menuItems[this.menuItems.length - 1].firstChild; - - if (e.shiftKey && document.activeElement === firstElement) { - e.preventDefault(); - lastElement?.focus(); - } else if (!e.shiftKey && document.activeElement === lastElement) { - e.preventDefault(); - firstElement.focus(); - } - } - } - - addListeners() { - this.hamburgerIcon.addEventListener('click', (e) => { - e.preventDefault(); - this.toggleMobileMenu(); - }); - - document.addEventListener('keydown', (e) => - this.handleDropdownKeyboardNavigation(e) - ); - } -} - -const mobileNav = new MobileNav(); -export { mobileNav }; diff --git a/src/scripts/modules/nav-sticky.js b/src/scripts/modules/nav-sticky.js deleted file mode 100644 index 564266faed..0000000000 --- a/src/scripts/modules/nav-sticky.js +++ /dev/null @@ -1,89 +0,0 @@ -// @ts-check - -import { qs } from './query.js'; - -/** - * Makes an existing navigation element sticky - * - * Example: If the existing navigation is not as tall as the content, the - * navigation will stick to the top, allowing the user to see it as - * they scroll through the article - * - * @param {string} headerSelector - * @param {string} navigationSelector - * @param {string} navigationListSelector - * @param {string} resizedEventName - */ -function addStickyNavigation( - headerSelector, - navigationSelector, - navigationListSelector, - resizedEventName -) { - function setNavigationMode() { - const header = qs(headerSelector); - const navigation = qs(navigationSelector); - const navigationList = qs(navigationListSelector); - - const buffer = 50; - const className = 'sticky'; - - const dimensions = { - browserHeight: window.innerHeight, - browserWidth: window.innerWidth, - headerHeight: header.clientHeight, - navigationHeight: navigationList.clientHeight, - }; - - // Only enable sticky mode if the menu will fit vertically - // && where the browser is more than 860px wide - if ( - dimensions.navigationHeight < - dimensions.browserHeight - - Math.max(dimensions.headerHeight, site_features.stickyNav.top) - - buffer && - dimensions.browserWidth > 860 - ) { - // Navigation: Sticky Mode - - navigation.classList.add(className); - const top = site_features.stickyNav.top ?? 220; - navigation.style.top = top + 'px'; - } else { - // Navigation: Fixed Mode - - navigation.classList.remove(className); - } - } - - function hideNavigation() { - const navigation = qs(navigationSelector); - - // Stop hiding the nav tree once the new nav's drawer is ready - if (document.documentElement.dataset.navDrawer === 'ready') { - navigation.style.display = ''; - return; - } - - if (window.innerWidth <= 1130) { - navigation.style.display = 'none'; - } else { - navigation.style.display = 'block'; - } - } - - setNavigationMode(); - hideNavigation(); - - document.addEventListener(resizedEventName, function (e) { - if (e.detail && e.detail.change && e.detail.change.height != 0) { - setNavigationMode(); - } - - if (e.detail.change.width != 0) { - hideNavigation(); - } - }); -} - -export { addStickyNavigation }; diff --git a/src/scripts/theme-switcher.ts b/src/scripts/theme-switcher.ts index 20dbecb2f6..27aecfddae 100644 --- a/src/scripts/theme-switcher.ts +++ b/src/scripts/theme-switcher.ts @@ -13,8 +13,6 @@ import { // Matches --duration-default in vars.css. const TRANSITION_MS = 300; -const CHECKBOX_SELECTOR = '[data-theme-toggle-checkbox]'; - const BUTTON_SELECTOR = '[data-theme-toggle-button]'; const BUTTON_ICON_SELECTOR = '.btn__icon'; @@ -26,10 +24,6 @@ const BUTTON_ICON_CLASSES: Record = { const root = document.documentElement; const darkQuery = window.matchMedia(COLOR_SCHEME_QUERY); -function checkboxes() { - return document.querySelectorAll(CHECKBOX_SELECTOR); -} - function buttons() { return document.querySelectorAll(BUTTON_SELECTOR); } @@ -67,10 +61,6 @@ function currentTheme(): Theme { } function syncControls(theme: Theme) { - checkboxes().forEach((box) => { - box.checked = theme === 'dark'; - }); - buttons().forEach((button) => { const icon = button.querySelector(BUTTON_ICON_SELECTOR); icon?.classList.remove( @@ -117,25 +107,6 @@ function bind() { // control rather than re-deriving it, so all switchers agree from paint one. syncControls(currentTheme()); - checkboxes().forEach((box) => { - if (box.dataset.themeBound) return; - box.dataset.themeBound = 'true'; - - // Covers mouse, touch, label clicks and Space. - box.addEventListener('change', () => - setTheme(box.checked ? 'dark' : 'light') - ); - - // A checkbox ignores Enter. The switch role treats it as an optional - // second activation key, and this control supported it before. Routed - // through click() so the change handler stays the only writer of state. - box.addEventListener('keydown', (event) => { - if (event.key !== 'Enter') return; - event.preventDefault(); - box.click(); - }); - }); - buttons().forEach((button) => { if (button.dataset.themeBound) return; button.dataset.themeBound = 'true'; diff --git a/src/scripts/top-nav-drawer.ts b/src/scripts/top-nav-drawer.ts index 64ce26ac30..1d04134e19 100644 --- a/src/scripts/top-nav-drawer.ts +++ b/src/scripts/top-nav-drawer.ts @@ -74,8 +74,6 @@ function placeNav(isCompact: boolean) { if (siteNav.parentElement !== navSlot) { navSlot.append(siteNav); } - // Undo nav-sticky.js hiding the tree - siteNav.style.display = ''; } else if (siteNav.parentElement !== navHome) { navHome.insertBefore(siteNav, navHomeNextSibling); } diff --git a/src/styles/main.css b/src/styles/main.css index 659e2d3e81..8e1ee10ab6 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -715,176 +715,6 @@ nav.skip-links a:focus { width: auto; } -/* Site Header */ - -.octo-header-bkg { - box-shadow: 0 5px 30px rgba(0, 0, 0, 0.2); - background-color: var(--header-bg); - min-height: var(--header-height); - position: fixed; - width: 100%; - top: 0; - /* z-index due to conflict with octo-title-bkg */ - z-index: 11; - background-color: var(--color-base-primary); - transition: background-color var(--duration-default) ease-in-out; - - @media (prefers-reduced-motion: reduce) { - transition: none; - } -} - -.octo-header { - margin-inline: var(--page-inline-margin); - font-size: var(--fontSizeBase); - z-index: 10; - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem 0; -} - -@media (max-width: 1130px) { - .octo-header { - gap: 0.5rem; - } -} - -.octo-header .logo { - max-height: 45px; -} - -.octo-header__logo { - height: 50px; - width: auto; -} - -.octo-header > a.navigation-icon { - display: none; - text-align: end; - stroke: var(--header-link-alt); - width: 40px; - height: 40px; -} - -@media (max-width: 1130px) { - .octo-header { - grid-template-columns: auto 40px; - } - - .octo-header > nav, - .octo-header > a.button--primary { - display: none; - } - - .octo-header > a.navigation-icon { - display: inline-block; - } -} - -.octo-header__wrapper--left { - display: flex; - align-items: center; - flex-wrap: nowrap; - column-gap: 0.5rem; -} - -/* Hide badge due to not enough space */ -@media (max-width: 360px) { - .octo-header__wrapper--left .badge { - display: none; - } -} - -/* Badge */ -.badge { - display: flex; - justify-content: center; - align-items: center; - padding: 0.5rem 0.75rem; - gap: 0.75rem; - border-radius: 0.5rem; - font-size: var(--fontSizeMedium); - font-weight: var(--fontWeight600); - line-height: 138%; - transition-property: background-color, color; - transition-timing-function: ease-in-out; - transition-duration: var(--duration-default); - - @media (prefers-reduced-motion: reduce) { - transition-property: none; - } -} - -.badge--brand { - color: var(--badge-color); - background-color: var(--badge-background); -} - -/* Theme switcher */ - -.theme-switcher { - position: relative; - display: inline-block; - width: 11.1875rem; - height: 2.5rem; - margin-inline-start: 1rem; -} - -/* Hide theme switcher in header on mobile */ -@media screen and (max-width: 1130px) { - .octo-header .octo-header__wrapper--left > .theme-switcher { - display: none; - } -} - -.site-nav__mobile > .theme-switcher { - display: block; - margin: 1rem auto; -} - -.theme-switcher input { - opacity: 0; - width: 0; - height: 0; - position: absolute; -} - -.theme-switcher__label { - display: flex; - align-items: center; - justify-content: space-around; - color: var(--white); - cursor: pointer; - width: 100%; - height: 100%; - background-color: var(--header-bg); - border: 0.0625rem solid var(--theme-switcher-border); - border-radius: 1.75rem; - position: relative; - transition: background-color var(--duration-default) ease-in-out; -} - -.switch-slider { - --toggle-sides-offset: 0.375rem; - position: absolute; - width: calc((100% - var(--toggle-sides-offset)) / 2); - height: calc(100% - var(--toggle-sides-offset)); - left: calc(var(--toggle-sides-offset) / 2); - background-color: var(--blue-500); - border-radius: 3.375rem; - transition: transform var(--duration-default) ease-in-out; -} - -.theme-switcher__icon { - font-size: var(--fontSizeMedium); - font-weight: var(--fontWeight700); - display: flex; - align-items: center; - gap: 0.25rem; - z-index: 2; -} - .theme-switcher__moon_icon { background-color: var(--colorIconPrimary); mask: url('../assets/icons/moon.svg') center / contain no-repeat; @@ -895,40 +725,6 @@ nav.skip-links a:focus { mask: url('../assets/icons/sun.svg') center / contain no-repeat; } -html[data-theme='dark'] .theme-switcher .switch-slider { - transform: translateX(100%); -} - -html[data-theme='dark'] .theme-switcher__icon--light { - color: #7c98b4; -} - -html[data-theme='dark'] .theme-switcher__icon--light svg path { - fill: #7c98b4; -} - -html[data-theme='light'] .theme-switcher__icon--dark { - color: #557999; -} - -html[data-theme='light'] .theme-switcher__icon--dark svg path { - fill: #557999; -} - -/* The input is visually hidden but still the focusable control, so the ring - has to be drawn on the label. */ -.theme-switcher input:focus-visible + .theme-switcher__label { - outline: 0.125rem solid var(--octo-blue); - outline-offset: 0.125rem; -} - -.theme-switcher input:disabled + .theme-switcher__label { - opacity: 0.5; - cursor: not-allowed; -} - -/* */ - .docs-home { text-align: center; margin-block-start: 5vh; @@ -1425,81 +1221,6 @@ html[data-theme='dark'] img.card__icon { } } -.mobile-menu { - display: none; -} - -@media (max-width: 1130px) { - .mobile-menu { - display: flex; - justify-content: center; - align-items: center; - width: var(--navbar-icon-size); - height: var(--navbar-icon-size); - } -} -.site-nav__mobile { - position: absolute; - width: 100%; - right: -100%; - transition: right 500ms ease-in-out; - margin: 0; - top: 5.125rem; -} - -.mobile-menu.is-active .site-nav__mobile { - right: 0; -} - -/* Burger menu */ -.hamburger { - position: relative; -} - -.hamburger:hover { - cursor: pointer; -} - -.hamburger .line { - width: var(--hamburger-line-width); - height: var(--hamburger-line-height); - background-color: var(--hamburger-lines-color); - display: block; - margin: var(--hamburger-line-margin) auto; - transition-property: opacity, transform; - transition-duration: 500ms; - border-radius: 0.1rem; -} -.hamburger .circle { - display: block; - width: var(--navbar-icon-size); - height: var(--navbar-icon-size); - border: solid #76a1c2; - border-radius: 50%; - border-width: 0; - background-color: transparent; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - opacity: 0; - transition-property: opacity, border-width; - transition-duration: 500ms; -} -.mobile-menu.is-active .hamburger .line:nth-child(1) { - transform: translateY(var(--hamburger-translate-y)) rotate(45deg); -} -.mobile-menu.is-active .hamburger .line:nth-child(2) { - opacity: 0; -} -.mobile-menu.is-active .hamburger .line:nth-child(3) { - transform: translateY(calc(-1 * var(--hamburger-translate-y))) rotate(-45deg); -} -.mobile-menu.is-active .hamburger .circle { - opacity: 1; - border-width: 0.0625rem; -} - /* Main content group */ /* The footer gets its own row so the nav can span both. The design runs the @@ -2185,14 +1906,6 @@ a[data-youtube] { } } -@media screen and (max-width: 1130px) { - .octo-header .site-search-results, - .octo-header .site-search__overlay, - .octo-header .site-search.is-active { - display: none; - } -} - .site-search { height: var(--search-height); border-radius: var(--search-border-radius); @@ -2432,27 +2145,6 @@ html:has(.site-search__wrapper.is-active) { display: none; } -@media (max-width: 1130px) { - .site-header .site-search__wrapper { - max-width: fit-content; - order: -1; - } - - .site-header .site-search { - display: none; - } - - .site-header .site-search__mobile { - display: flex; - width: var(--navbar-icon-size); - height: var(--navbar-icon-size); - border-radius: calc(var(--navbar-icon-size) / 2); - justify-content: center; - align-items: center; - border: var(--search-border); - } -} - .site-search .show-more { display: inline-block; font-size: var(--fontSizeMedium); @@ -2936,10 +2628,6 @@ a.button.button--primary:active { box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.35) inset; } -.octo-header a.button { - width: 120px; -} - /* Tooltip. Put the label in data-tooltip; the host positions the bubble above itself, so it needs room there and a stacking context of its own. */ [data-tooltip] { @@ -3074,26 +2762,6 @@ a.button.button--primary:active { } /* TopNav component */ -.new-top-nav { - display: none; -} - -html[data-nav='new'] { - --header-height: 68px; -} - -html[data-nav='new'] .new-top-nav { - display: block; - position: fixed; - width: 100%; - top: 0; - z-index: 11; -} - -html[data-nav='new'] .site-header { - display: none; -} - .top-nav__bars-icon, .top-nav__close-icon { background-color: var(--colorIconPrimary); @@ -3105,13 +2773,11 @@ html[data-nav='new'] .site-header { } @media (max-width: 1130px) { - html[data-nav='new'][data-nav-drawer='ready'] .content-group .site-nav { + html[data-nav-drawer='ready'] .content-group .site-nav { display: none; } - html[data-nav='new'][data-nav-drawer='ready'] - .skip-links - a[href='#site-nav'] { + html[data-nav-drawer='ready'] .skip-links a[href='#site-nav'] { display: none; } } diff --git a/src/styles/vars.css b/src/styles/vars.css index e566ac3607..c56e23d592 100644 --- a/src/styles/vars.css +++ b/src/styles/vars.css @@ -163,23 +163,8 @@ --search-placeholder-color: #173b56; --search-remove-icon-color: #355670; --search-overlay: rgba(12, 26, 36, 0.3); - --navbar-icon-size: 3rem; - - /* Hamburger menu */ - --hamburger-line-width: 1.5rem; - --hamburger-line-height: 0.2rem; - --hamburger-line-margin: 0.2rem; - --hamburger-translate-y: calc( - var(--hamburger-line-height) + var(--hamburger-line-margin) - ); - --hamburger-lines-color: var(--colorIconPrimary); - /* Theme Switcher */ - --theme-switcher-border: var(--colorBorderPrimary); - - /* Badge */ - --badge-background: rgba(162, 209, 244, 0.2); - --badge-color: #0d80d8; + --hamburger-lines-color: var(--colorIconPrimary); /* Card */ --card-shadow: 0 10px 20px 0 rgba(0, 0, 0, 0.15); @@ -193,9 +178,7 @@ --octo-blue-lighter: #2f95e3ff; --octo-blue-lightest: #1fc0ffff; - --header-bg: var(--colorBackgroundPrimaryDefault); --header-link-color: var(--octo-blue); - --header-link-alt: var(--blue-gray-darker); --content-bg: var(--white); @@ -211,7 +194,7 @@ --block-gap: 2rem; --header-pin: 0; - --header-height: 82px; + --header-height: 68px; --scroll-pad: 140px; --box-shadow: 4px 0px 8px rgba(0, 0, 0, 0.25); @@ -304,12 +287,9 @@ html[data-theme='dark'] { /* TODO: --blue-grey is hand-themed for .card__description, which is secondary text, so --color-text-secondary already covers it. */ --blue-grey: #dae2e9ff; - --header-link-alt: var(--color-text); --grey-light: var(--navy-700); --octopus-logo-text-color: var(--white); --search-overlay: rgba(12, 26, 36, 0.8); - --badge-background: rgba(31, 192, 255, 0.1); - --badge-color: var(--octo-blue-lightest); /* Tiles sit one step above the page so hover has somewhere lighter to go */ --icon-tile-background-hover: var(--colorBackgroundTertiary); diff --git a/tests/profile-menu.spec.ts b/tests/profile-menu.spec.ts index e5612c743f..8c377fb616 100644 --- a/tests/profile-menu.spec.ts +++ b/tests/profile-menu.spec.ts @@ -1,6 +1,6 @@ import { test, expect, type Page } from '@playwright/test'; -const docsPage = '/docs/?newnav'; +const docsPage = '/docs/'; // Match the full width avatar menu; the inline one used when the page narrows is tested in // topnav-responsive.spec.ts diff --git a/tests/theme.spec.ts b/tests/theme.spec.ts index 92a30c2e87..273a78caf6 100644 --- a/tests/theme.spec.ts +++ b/tests/theme.spec.ts @@ -3,10 +3,9 @@ import { test, expect, type Page } from '@playwright/test'; const home = '/docs/'; const otherPage = '/docs/getting-started/'; -const headerInput = '#theme-switcher'; -const headerLabel = 'label[for="theme-switcher"]'; -const mobileInput = '#theme-switcher-mobile'; -const slider = '#theme-switcher ~ .theme-switcher__label .switch-slider'; +const toggle = '.top-nav__trailing [data-theme-toggle-button]'; +const toggleIcon = `${toggle} .btn__icon`; +const drawerToggleIcon = '.top-nav__drawer-theme .btn__icon'; // Auto-retrying so a slow style/attribute write can never make these flaky. function expectTheme(page: Page, value: string) { @@ -20,8 +19,10 @@ function expectPreference(page: Page, value: string) { ); } -function sliderTransform(page: Page) { - return page.locator(slider).evaluate((el) => getComputedStyle(el).transform); +function expectToggleOffers(page: Page, theme: string) { + return expect(page.locator(toggleIcon)).toHaveClass( + new RegExp(`theme-switcher__${theme === 'dark' ? 'moon' : 'sun'}_icon`) + ); } test.describe('theme', () => { @@ -33,21 +34,21 @@ test.describe('theme', () => { await expectTheme(page, 'dark'); await expectPreference(page, 'system'); - await expect(page.locator(headerInput)).toBeChecked(); + await expectToggleOffers(page, 'light'); }); test('an explicit choice wins and survives navigation', async ({ page, }) => { await page.goto(home); - await page.locator(headerLabel).click(); + await page.locator(toggle).click(); await expectTheme(page, 'light'); await expectPreference(page, 'light'); await page.goto(otherPage); await expectTheme(page, 'light'); - await expect(page.locator(headerInput)).not.toBeChecked(); + await expectToggleOffers(page, 'dark'); }); test('falls back to the OS when storage throws', async ({ page }) => { @@ -63,7 +64,7 @@ test.describe('theme', () => { await expectTheme(page, 'dark'); // Still usable for the life of the page, just not persisted. - await page.locator(headerLabel).click(); + await page.locator(toggle).click(); await expectTheme(page, 'light'); }); }); @@ -75,38 +76,20 @@ test.describe('theme', () => { await page.goto(home); await expectTheme(page, 'light'); - await expect(page.locator(headerInput)).not.toBeChecked(); + await expectToggleOffers(page, 'dark'); }); - test('both switchers stay in sync', async ({ page }) => { + test('every switcher on the page stays in sync', async ({ page }) => { await page.goto(home); - await page.locator(headerLabel).click(); + await page.locator(toggle).click(); await expectTheme(page, 'dark'); - await expect(page.locator(headerInput)).toBeChecked(); - await expect(page.locator(mobileInput)).toBeChecked(); - - await page.locator(headerLabel).click(); - await expectTheme(page, 'light'); - await expect(page.locator(mobileInput)).not.toBeChecked(); + await expectToggleOffers(page, 'light'); + // The drawer's toggle is the one used when the page narrows. + await expect(page.locator(drawerToggleIcon)).toHaveClass( + /theme-switcher__sun_icon/ + ); }); - - // Space is the native checkbox key; Enter is optional for role="switch" - // but the control supported it before, so both have to keep working. - for (const key of ['Space', 'Enter']) { - test(`is operable with ${key}`, async ({ page }) => { - await page.goto(home); - await page.locator(headerInput).focus(); - - await page.keyboard.press(key); - await expectTheme(page, 'dark'); - await expect(page.locator(headerInput)).toBeChecked(); - - await page.keyboard.press(key); - await expectTheme(page, 'light'); - await expect(page.locator(headerInput)).not.toBeChecked(); - }); - } }); // The design token stylesheets define their custom properties only under @@ -124,24 +107,4 @@ test.describe('theme', () => { await expectTheme(page, 'light'); }); }); - - // Regression guard: the knob used to be driven by a JS-applied class, so it - // painted on the light side and slid across after hydration. Driving it from - // data-theme means the attribute alone decides the position. - test('the knob position is driven by data-theme, not by script', async ({ - page, - }) => { - await page.goto(home); - - // Polled because the slider transitions between the two positions. - await page.evaluate(() => - document.documentElement.setAttribute('data-theme', 'dark') - ); - await expect.poll(() => sliderTransform(page)).not.toBe('none'); - - await page.evaluate(() => - document.documentElement.setAttribute('data-theme', 'light') - ); - await expect.poll(() => sliderTransform(page)).toBe('none'); - }); }); diff --git a/tests/topnav-responsive.spec.ts b/tests/topnav-responsive.spec.ts index 353d434eb4..1d087f4e3f 100644 --- a/tests/topnav-responsive.spec.ts +++ b/tests/topnav-responsive.spec.ts @@ -1,7 +1,7 @@ import { test, expect, type Page } from '@playwright/test'; -const docsPage = '/docs/?newnav'; -const sectionPage = '/docs/getting-started/?newnav'; +const docsPage = '/docs/'; +const sectionPage = '/docs/getting-started/'; const nav = '.top-nav'; const inlineLinks = `${nav} .top-nav__links`; diff --git a/tests/topnav-signedin.spec.ts b/tests/topnav-signedin.spec.ts index 22c9ed2263..88c9422b04 100644 --- a/tests/topnav-signedin.spec.ts +++ b/tests/topnav-signedin.spec.ts @@ -1,6 +1,6 @@ import { test, expect, type Page } from '@playwright/test'; -const docsPage = '/docs/?newnav'; +const docsPage = '/docs/'; const nav = '.top-nav'; const trailing = `${nav} .top-nav__trailing`;