From 1e1f3f5f61ba67b784d7869ad277d1a42d752bad Mon Sep 17 00:00:00 2001 From: Rakibei Date: Sat, 8 Aug 2026 01:24:44 +0200 Subject: [PATCH 1/4] config: reworked config css --- ui/config/config.css | 428 ++++++++++++++++++++++++++++++++---------- ui/config/config.html | 13 +- ui/config/config.ts | 120 +++++++++--- 3 files changed, 428 insertions(+), 133 deletions(-) diff --git a/ui/config/config.css b/ui/config/config.css index 5130c393f1f..d443f6dd790 100644 --- a/ui/config/config.css +++ b/ui/config/config.css @@ -3,13 +3,35 @@ html { margin: 0; } +:root { + color-scheme: light; + + --bg: #f4f6f9; + --surface: #fff; + --surface-muted: #f8fafc; + --border: #d9e0e8; + --border-strong: #c4ced9; + --text: #1d2733; + --text-muted: #657384; + --accent: #4f6bed; + --accent-hover: #4059d6; + --accent-soft: #eef2ff; + --radius: 10px; +} + html { - height: 100%; + min-height: 100%; overflow-y: scroll; - background-color: white; - font-family: Helvetica, sans-serif; + background: var(--bg); + font-family: Inter, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 14px; - font-weight: 300; + font-weight: 400; + color: var(--text); +} + +body { + min-height: 100vh; + background: linear-gradient(180deg, #f8fafc 0, var(--bg) 240px); } #container { @@ -17,8 +39,7 @@ html { } #butter-margin { - position: relative; - margin: 60px; + height: 64px; } #butter-margin.hidden { @@ -27,33 +48,132 @@ html { #butter-bar { position: fixed; + z-index: 1000; top: 0; left: 0; - padding-left: 12px; - padding-right: 12px; - width: calc(100% - 24px); - border-bottom: 1px solid black; - height: 50px; - background-color: #fff37b; - overflow: hidden; + box-sizing: border-box; + width: 100%; + min-height: 56px; + padding: 10px max(20px, calc((100% - 1180px) / 2)); + border-bottom: 1px solid #eadca1; + background: #fff8d8; + box-shadow: 0 4px 16px rgb(79 65 18 / 8%); display: grid; - grid-template-columns: repeat(1, auto auto); - grid-gap: 12px; + grid-template-columns: 1fr auto; + gap: 16px; + align-items: center; } .reload-text { - align-self: center; - justify-self: start; + color: #685919; + font-weight: 500; } .reload-button { - padding: 8px; - align-self: center; - justify-self: end; + padding: 8px 14px; + border: 1px solid #d8c56e; + border-radius: 7px; + background: #fff; + color: #594b12; + font: inherit; + font-weight: 600; + cursor: pointer; } -.overlay-container { +.reload-button:hover { + background: #fffdf3; +} + +.config-title { + color: var(--toolbar-accent); + font-size: 23px; + font-weight: 700; + letter-spacing: -0.02em; + transition: color 180ms ease; +} + +.config-toolbar { + --toolbar-bg: linear-gradient(135deg, #eef2ff 0%, rgb(255 255 255 / 97%) 72%); + + position: sticky; + z-index: 20; + top: 0; + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(220px, 320px); + gap: 24px; + align-items: end; margin-bottom: 20px; + padding: 18px 20px; + border-radius: var(--radius); + background: var(--toolbar-bg); + transition: background 180ms ease, border-color 180ms ease, box-shadow 180ms ease; +} + +/* Top-level category themes. Unknown/custom groups keep the default indigo theme above. */ +.config-toolbar[data-category="general"] { + --toolbar-bg: linear-gradient(135deg, #eaf3ff 0%, rgb(255 255 255 / 97%) 72%); +} + +.config-toolbar[data-category="raidboss"] { + --toolbar-bg: linear-gradient(135deg, #fff0f0 0%, rgb(255 255 255 / 97%) 72%); +} + +.config-toolbar[data-category="jobs"] { + --toolbar-bg: linear-gradient(135deg, #e9fbf7 0%, rgb(255 255 255 / 97%) 72%); +} + +.config-toolbar[data-category="eureka"] { + --toolbar-bg: linear-gradient(135deg, #f4efff 0%, rgb(255 255 255 / 97%) 72%); +} + +.config-toolbar[data-category="oopsyraidsy"] { + --toolbar-bg: linear-gradient(135deg, #fff4e8 0%, rgb(255 255 255 / 97%) 72%); +} + +.config-toolbar[data-category="radar"] { + --toolbar-bg: linear-gradient(135deg, #ecfdf3 0%, rgb(255 255 255 / 97%) 72%); +} + +.config-subtitle { + margin-top: 4px; + color: var(--text-muted); + font-size: 13px; +} + +.category-selector-container { + display: grid; + gap: 6px; +} + +.category-selector-label { + color: var(--text-muted); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.category-selector { + width: 100%; +} + +.config-content { + min-width: 0; +} + +.overlay-container { + overflow: hidden; +} + +.overlay-container.category-hidden { + display: none; +} + +.overlay-anchor { + display: block; + position: relative; + top: -92px; + visibility: hidden; } .collapsed > :not(:first-child) { @@ -61,73 +181,71 @@ html { } .overlay-header { - background-color: lightblue; - padding: 12px; - text-transform: uppercase; - border: 1px solid black; - cursor: pointer; + padding: 18px 22px; + border-bottom: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); font-size: 18px; + font-weight: 700; + letter-spacing: -0.01em; +} - /* use grid to vertically center */ +.overlay-options { display: grid; - grid-template-columns: repeat(1, auto 1fr); - justify-items: left; - align-items: center; + grid-template-columns: minmax(220px, 340px) minmax(220px, 1fr); + gap: 0; + padding: 8px 22px 18px; } -.overlay-header::before { - content: ""; +.overlay-options > div { + min-width: 0; box-sizing: border-box; - margin-right: 5px; - width: 10px; - height: 10px; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-top: 10px solid black; + padding-top: 13px; + padding-bottom: 13px; } -.collapsed .overlay-header::before { - border-top: 5px solid transparent; - border-bottom: 5px solid transparent; - border-left: 10px solid black; - border-right: none; +.overlay-options > div:nth-last-child(-n + 2) { + border-bottom: none; } -.overlay-options { - display: grid; - margin: 12px 0 12px 12px; - margin-left: 20px; - grid-template-columns: repeat(1, auto 1fr); - grid-gap: 12px; -} - -/* vertically center option names */ +/* Vertically center option names. */ .option-name { display: grid; align-items: center; + min-height: 34px; + padding-right: 24px; + font-weight: 600; + color: #273444; } .option-input-container { display: grid; align-items: center; + justify-items: start; } .trigger-expansion-container { - margin-bottom: 8px; grid-column-end: span 2; + margin: 10px 0; + overflow: hidden; + border: 1px solid var(--border); + border-radius: 8px; + background: #eef2f8; } .trigger-expansion-header { - background-color: #c9cde2; - padding: 8px; - border: 1px solid black; + padding: 11px 13px; + border-bottom: 1px solid transparent; cursor: pointer; - - /* use grid to vertically center */ display: grid; - grid-template-columns: repeat(1, auto 1fr); + grid-template-columns: auto 1fr; justify-items: left; align-items: center; + font-weight: 700; +} + +.trigger-expansion-container:not(.collapsed) > .trigger-expansion-header { + border-bottom-color: var(--border); } .trigger-expansion-header::before { @@ -138,30 +256,39 @@ html { height: 6px; border-left: 3px solid transparent; border-right: 3px solid transparent; - border-top: 6px solid black; + border-top: 6px solid #667587; } .collapsed .trigger-expansion-header::before { border-top: 3px solid transparent; border-bottom: 3px solid transparent; - border-left: 6px solid black; + border-left: 6px solid #667587; border-right: none; } .trigger-file-container { - margin: 12px 0 12px 12px; + margin: 10px 12px; grid-column-end: span 2; + overflow: hidden; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--surface); } .trigger-file-header { - background-color: wheat; - padding: 8px; - border: 1px solid black; + padding: 10px 12px; + border-bottom: 1px solid transparent; + background: #fff7e9; cursor: pointer; display: grid; - grid-template-columns: repeat(1, auto auto 1fr auto); + grid-template-columns: auto auto 1fr auto; justify-items: right; align-items: center; + gap: 8px; +} + +.trigger-file-container:not(.collapsed) > .trigger-file-header { + border-bottom-color: #eadfc8; } .trigger-file-header::before { @@ -172,47 +299,53 @@ html { height: 6px; border-left: 3px solid transparent; border-right: 3px solid transparent; - border-top: 6px solid black; + border-top: 6px solid #667587; } .collapsed .trigger-file-header::before { border-top: 3px solid transparent; border-bottom: 3px solid transparent; - border-left: 6px solid black; + border-left: 6px solid #667587; border-right: none; } .trigger-file-header-part { - /* pretending not to have a grid gap on the left */ - margin-right: 20px; + margin-right: 12px; } .trigger-set-override-warning { - padding: 20px; + padding: 14px 16px; text-align: center; - color: #856404; - background-color: #fff3cd; - border: 1px solid #ffc107; - border-radius: 4px; - margin: 10px; + color: #765c09; + background: #fff8dc; + border: 1px solid #ead47b; + border-radius: 8px; + margin: 12px; } .timeline-edit-container { - margin: 12px 0 12px 12px; + margin: 12px; grid-column-end: span 2; + overflow: hidden; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--surface-muted); } .timeline-edit-header { - background-color: #ebebeb; - padding: 5px; - border: 1px solid black; + padding: 10px 12px; + border-bottom: 1px solid transparent; + background: #f1f4f8; cursor: pointer; - - /* use grid to vertically center */ display: grid; - grid-template-columns: repeat(1, auto 1fr); + grid-template-columns: auto 1fr; justify-items: left; align-items: center; + font-weight: 600; +} + +.timeline-edit-container:not(.collapsed) > .timeline-edit-header { + border-bottom-color: var(--border); } .timeline-edit-header::before { @@ -223,13 +356,13 @@ html { height: 6px; border-left: 3px solid transparent; border-right: 3px solid transparent; - border-top: 6px solid black; + border-top: 6px solid #667587; } .collapsed .timeline-edit-header::before { border-top: 3px solid transparent; border-bottom: 3px solid transparent; - border-left: 6px solid black; + border-left: 6px solid #667587; border-right: none; } @@ -265,10 +398,11 @@ html { overflow: auto; resize: both; height: 300px; - border: solid black 1px; - margin: 12px 0 24px 24px; - padding: 8px; - background-color: #ebebeb; + border: 1px solid var(--border); + border-radius: 7px; + margin: 12px 24px 24px; + padding: 10px; + background: #fff; } .timeline-add-header { @@ -309,8 +443,8 @@ html { .trigger-file-options { display: grid; margin: 12px; - grid-template-columns: repeat(1, auto 1fr); - grid-gap: 20px; + grid-template-columns: minmax(180px, auto) 1fr; + gap: 14px 20px; } .trigger.disabled { @@ -355,10 +489,11 @@ html { } .comment { - margin-top: 8px; - margin-left: 8px; + margin-top: 6px; font-size: 12px; - max-width: calc(18em + 8px); + line-height: 1.45; + color: var(--text-muted); + max-width: 32em; word-wrap: break-word; hyphens: auto; } @@ -393,19 +528,50 @@ html { color: grey; } +select { + width: fit-content; + max-width: 100%; +} + .input-dir-container { display: grid; - grid-template-columns: repeat(1, auto 1fr); - grid-gap: 10px; + grid-template-columns: auto minmax(0, 1fr); + gap: 10px; align-items: center; } input[type="text"] { - padding: 3px; - width: 40px; + width: 64px; text-align: right; } +input[type="text"], +select { + box-sizing: border-box; + min-height: 36px; + padding: 7px 10px; + border: 1px solid var(--border-strong); + border-radius: 7px; + outline: none; + background: #fff; + color: var(--text); + font: inherit; + transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease; +} + +input[type="text"]:focus, +select:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px rgb(79 107 237 / 14%); +} + +input[type="text"]:disabled, +select:disabled { + background: #f2f4f7; + color: #8a95a3; + cursor: not-allowed; +} + .trigger-duration > input[type="text"] { width: 75px; text-align: left; @@ -427,15 +593,69 @@ input[type="text"].input-string-field { } input[type="submit"] { - padding: 3px; + min-height: 36px; + padding: 7px 12px; + border: 1px solid var(--border-strong); + border-radius: 7px; + background: #fff; + color: var(--text); + font: inherit; + font-weight: 600; + cursor: pointer; } -select { - padding: 3px; - width: fit-content; +input[type="submit"]:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +input[type="submit"]:hover:not(:disabled) { + border-color: #aeb9c6; + background: #f8fafc; } input[type="checkbox"] { - width: 15px; - height: 15px; + width: 18px; + height: 18px; + margin: 0; + accent-color: var(--accent); + cursor: pointer; +} + +input[type="checkbox"]:disabled { + cursor: not-allowed; +} + +.input-dir-label { + min-width: 0; + overflow-wrap: anywhere; + color: var(--text-muted); +} + +@media (max-width: 760px) { + #container { + width: min(100% - 24px, 1180px); + padding-top: 12px; + } + + .config-toolbar { + position: static; + grid-template-columns: 1fr; + gap: 14px; + } + + .overlay-options { + grid-template-columns: minmax(150px, 42%) minmax(0, 1fr); + padding-right: 14px; + padding-left: 14px; + } + + .option-name { + padding-right: 14px; + } + + .timeline-text-container, + .timeline-add-container { + margin-left: 12px; + } } diff --git a/ui/config/config.html b/ui/config/config.html index 4e95439e7c4..7ea81f56e85 100644 --- a/ui/config/config.html +++ b/ui/config/config.html @@ -1,12 +1,13 @@ - Cactbot Configuration - + Cactbot Configuration + + - -
+ +
diff --git a/ui/config/config.ts b/ui/config/config.ts index 3520a5f86da..ff9a6b2aaca 100644 --- a/ui/config/config.ts +++ b/ui/config/config.ts @@ -619,15 +619,77 @@ export class CactbotConfigurator { // Top level UI builder, builds everything. buildUI(container: HTMLElement, contents: ConfigContents): void { - for (const group in contents) { - const content = contents[group]; - if (!content || content.length === 0) - continue; + const groups = Object.entries(contents).filter(([, content]) => content.length > 0); + const firstGroup = groups[0]?.[0]; + if (firstGroup === undefined || firstGroup === '') + return; + + const formatGroupName = (group: string): string => { + const displayNames: { [group: string]: string } = { + general: 'General', + raidboss: 'Raidboss', + jobs: 'Jobs', + eureka: 'Eureka', + oopsyraidsy: 'Oopsyraidsy', + radar: 'Radar', + }; + return displayNames[group] ?? group + .replace(/[-_]/g, ' ') + .replace(/(?:^|\s)\S/g, (c) => c.toUpperCase()); + }; + + // A compact category selector keeps the configurator from rendering as one very long page. + const toolbar = document.createElement('div'); + toolbar.classList.add('config-toolbar'); + container.appendChild(toolbar); + + const titleContainer = document.createElement('div'); + titleContainer.classList.add('config-title-container'); + toolbar.appendChild(titleContainer); + + const title = document.createElement('div'); + title.classList.add('config-title'); + title.innerText = 'Cactbot Configuration'; + titleContainer.appendChild(title); + + const subtitle = document.createElement('div'); + subtitle.classList.add('config-subtitle'); + subtitle.innerText = 'Choose a category to edit its settings.'; + titleContainer.appendChild(subtitle); + + const selectorContainer = document.createElement('label'); + selectorContainer.classList.add('category-selector-container'); + toolbar.appendChild(selectorContainer); + + const selectorLabel = document.createElement('span'); + selectorLabel.classList.add('category-selector-label'); + selectorLabel.innerText = 'Category'; + selectorContainer.appendChild(selectorLabel); + + const selector = document.createElement('select'); + selector.classList.add('category-selector'); + selectorContainer.appendChild(selector); + + const contentContainer = document.createElement('div'); + contentContainer.classList.add('config-content'); + container.appendChild(contentContainer); + + const groupContainers = new Map(); + + for (const [group, content] of groups) { + const option = document.createElement('option'); + option.value = group; + option.innerText = formatGroupName(group); + selector.appendChild(option); // For each overlay options template, build a section for it. // Then iterate through all of its options and build ui for those options. // Give each options template a chance to build special ui. - const groupDiv = this.buildOverlayGroup(container, group); + const groupDiv = this.buildOverlayGroup(contentContainer, group, formatGroupName(group)); + const groupContainer = groupDiv.parentElement; + if (!(groupContainer instanceof HTMLElement)) + throw new UnreachableCode(); + groupContainers.set(group, groupContainer); // Track the values of and html elements associated with each config entry, // to be used for updating visibility/enablement of config entries. @@ -664,34 +726,46 @@ export class CactbotConfigurator { // Once the group is fully created, process visibility settings immediately. this.updateVisibility(elements, values); } + + const showGroup = (group: string): void => { + for (const [key, groupContainer] of groupContainers) + groupContainer.classList.toggle('category-hidden', key !== group); + + // Let CSS theme the toolbar based on the currently selected category. + toolbar.dataset.category = group; + + selector.value = group; + if (window.location.hash !== `#${group}`) + window.history.replaceState(undefined, '', `#${group}`); + }; + + selector.onchange = () => showGroup(selector.value); + + // Preserve direct links such as config.html#raidboss when possible. + const hashGroup = window.location.hash.slice(1); + showGroup(groupContainers.has(hashGroup) ? hashGroup : firstGroup); } // Overlay builder for each overlay type (e.g. raidboss, jobs). - buildOverlayGroup(container: HTMLElement, group: string): HTMLElement { - const collapser = document.createElement('div'); - collapser.classList.add('overlay-container'); - container.appendChild(collapser); + buildOverlayGroup(container: HTMLElement, group: string, displayName = group): HTMLElement { + const section = document.createElement('section'); + section.classList.add('overlay-container'); + section.dataset.group = group; + container.appendChild(section); - const a = document.createElement('a'); - // TODO: fix me - /* eslint-disable-next-line deprecation/deprecation */ - a.name = group; - collapser.appendChild(a); + const anchor = document.createElement('a'); + anchor.id = group; + anchor.classList.add('overlay-anchor'); + section.appendChild(anchor); const header = document.createElement('div'); header.classList.add('overlay-header'); - header.innerText = group; - a.appendChild(header); + header.innerText = displayName; + section.appendChild(header); const groupDiv = document.createElement('div'); groupDiv.classList.add('overlay-options'); - collapser.appendChild(groupDiv); - - a.onclick = () => { - const parent = a.parentNode; - if (parent instanceof HTMLElement) - parent.classList.toggle('collapsed'); - }; + section.appendChild(groupDiv); return groupDiv; } From c4aca42498d6580ebe3bea4d646e1e869564b407 Mon Sep 17 00:00:00 2001 From: Rakibei Date: Sat, 8 Aug 2026 01:26:48 +0200 Subject: [PATCH 2/4] config: revert config html --- ui/config/config.html | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ui/config/config.html b/ui/config/config.html index 7ea81f56e85..4e95439e7c4 100644 --- a/ui/config/config.html +++ b/ui/config/config.html @@ -1,13 +1,12 @@ - Cactbot Configuration - - + Cactbot Configuration + - -
+ +
From 6a3b6e9deb022abac35fcc693ade5f6075680ce3 Mon Sep 17 00:00:00 2001 From: Rakibei Date: Sat, 8 Aug 2026 20:14:57 +0200 Subject: [PATCH 3/4] config: centered category select --- ui/config/config.css | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/config/config.css b/ui/config/config.css index d443f6dd790..5a786a13bf2 100644 --- a/ui/config/config.css +++ b/ui/config/config.css @@ -99,7 +99,7 @@ body { z-index: 20; top: 0; display: grid; - grid-template-columns: minmax(0, 1fr) minmax(220px, 320px); + grid-template-columns: minmax(0, 1fr) minmax(220px, 400px) minmax(0, 1fr); gap: 24px; align-items: end; margin-bottom: 20px; @@ -111,27 +111,27 @@ body { /* Top-level category themes. Unknown/custom groups keep the default indigo theme above. */ .config-toolbar[data-category="general"] { - --toolbar-bg: linear-gradient(135deg, #eaf3ff 0%, rgb(255 255 255 / 97%) 72%); + --toolbar-bg: linear-gradient(135deg, #87bbff 0%, rgb(255 255 255 / 97%) 72%); } .config-toolbar[data-category="raidboss"] { - --toolbar-bg: linear-gradient(135deg, #fff0f0 0%, rgb(255 255 255 / 97%) 72%); + --toolbar-bg: linear-gradient(135deg, #fc5151 0%, rgb(255 255 255 / 97%) 72%); } .config-toolbar[data-category="jobs"] { - --toolbar-bg: linear-gradient(135deg, #e9fbf7 0%, rgb(255 255 255 / 97%) 72%); + --toolbar-bg: linear-gradient(135deg, #faf757 0%, rgb(255 255 255 / 97%) 72%); } .config-toolbar[data-category="eureka"] { - --toolbar-bg: linear-gradient(135deg, #f4efff 0%, rgb(255 255 255 / 97%) 72%); + --toolbar-bg: linear-gradient(135deg, #a077fc 0%, rgb(255 255 255 / 97%) 72%); } .config-toolbar[data-category="oopsyraidsy"] { - --toolbar-bg: linear-gradient(135deg, #fff4e8 0%, rgb(255 255 255 / 97%) 72%); + --toolbar-bg: linear-gradient(135deg, #f7a74f 0%, rgb(255 255 255 / 97%) 72%); } .config-toolbar[data-category="radar"] { - --toolbar-bg: linear-gradient(135deg, #ecfdf3 0%, rgb(255 255 255 / 97%) 72%); + --toolbar-bg: linear-gradient(135deg, #78faad 0%, rgb(255 255 255 / 97%) 72%); } .config-subtitle { From 9686e6fea05efb50631056e9b7bc92a1f387451c Mon Sep 17 00:00:00 2001 From: Rakibei Date: Mon, 10 Aug 2026 12:50:49 +0200 Subject: [PATCH 4/4] config: changed padding --- ui/config/config.css | 15 +++++++++++---- ui/config/config.ts | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/ui/config/config.css b/ui/config/config.css index 5a786a13bf2..96c06e84498 100644 --- a/ui/config/config.css +++ b/ui/config/config.css @@ -95,7 +95,6 @@ body { .config-toolbar { --toolbar-bg: linear-gradient(135deg, #eef2ff 0%, rgb(255 255 255 / 97%) 72%); - position: sticky; z-index: 20; top: 0; display: grid; @@ -162,7 +161,7 @@ body { } .overlay-container { - overflow: hidden; + overflow: visible; } .overlay-container.category-hidden { @@ -188,6 +187,14 @@ body { font-size: 18px; font-weight: 700; letter-spacing: -0.01em; + z-index: 20; + position: sticky; + top: 0; +} + +.overlay-header.is-stuck { + border-top-left-radius: 0; + border-top-right-radius: 0; } .overlay-options { @@ -200,8 +207,8 @@ body { .overlay-options > div { min-width: 0; box-sizing: border-box; - padding-top: 13px; - padding-bottom: 13px; + padding-top: 3px; + padding-bottom: 3px; } .overlay-options > div:nth-last-child(-n + 2) { diff --git a/ui/config/config.ts b/ui/config/config.ts index ff9a6b2aaca..ac8dc142bf9 100644 --- a/ui/config/config.ts +++ b/ui/config/config.ts @@ -763,6 +763,20 @@ export class CactbotConfigurator { header.innerText = displayName; section.appendChild(header); + const updateStickyState = () => { + const sectionRect = section.getBoundingClientRect(); + const headerRect = header.getBoundingClientRect(); + + const isStuck = sectionRect.top < 0 && + headerRect.top <= 0 && + sectionRect.bottom > headerRect.height; + + header.classList.toggle('is-stuck', isStuck); + }; + + window.addEventListener('scroll', updateStickyState, { passive: true }); + updateStickyState(); + const groupDiv = document.createElement('div'); groupDiv.classList.add('overlay-options'); section.appendChild(groupDiv);