@astronox/core 0.4.13 → 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astronox/core",
3
- "version": "0.4.13",
3
+ "version": "0.4.14",
4
4
  "description": "Reusable Astro components, patterns, styles, and infrastructure for AstroNox sites.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -4,27 +4,27 @@ const noop = (): void => undefined;
4
4
  export function initScrollToTop(control: HTMLButtonElement): () => void {
5
5
  if (control.getAttribute(initializedAttribute) === 'true') return noop;
6
6
 
7
- const viewport = document.querySelector<HTMLElement>('[data-site-viewport]');
8
- if (!viewport) return noop;
7
+ const scrollRoot = document.scrollingElement;
8
+ if (!scrollRoot) return noop;
9
9
 
10
10
  control.setAttribute(initializedAttribute, 'true');
11
11
 
12
12
  const sync = (): void => {
13
13
  control.hidden =
14
- viewport.scrollTop <= 0 || document.fullscreenElement !== null;
14
+ scrollRoot.scrollTop <= 0 || document.fullscreenElement !== null;
15
15
  };
16
16
 
17
17
  const scrollToTop = (): void => {
18
- viewport.scrollTo({ top: 0, behavior: 'auto' });
18
+ window.scrollTo({ top: 0, behavior: 'auto' });
19
19
  };
20
20
 
21
- viewport.addEventListener('scroll', sync, { passive: true });
21
+ window.addEventListener('scroll', sync, { passive: true });
22
22
  document.addEventListener('fullscreenchange', sync);
23
23
  control.addEventListener('click', scrollToTop);
24
24
  sync();
25
25
 
26
26
  return () => {
27
- viewport.removeEventListener('scroll', sync);
27
+ window.removeEventListener('scroll', sync);
28
28
  document.removeEventListener('fullscreenchange', sync);
29
29
  control.removeEventListener('click', scrollToTop);
30
30
  control.removeAttribute(initializedAttribute);
@@ -16,7 +16,7 @@
16
16
  .card-header[data-sticky-surface] {
17
17
  position: sticky;
18
18
  z-index: 41;
19
- top: var(--nox-layout-gap);
19
+ top: calc(var(--nox-sticky-viewport-offset) + var(--nox-layout-gap));
20
20
  }
21
21
 
22
22
  .card-header__layout {
@@ -103,6 +103,18 @@
103
103
  grid-template-columns: minmax(0, 7fr) minmax(0, 3fr);
104
104
  }
105
105
 
106
+ .grid[data-layout='content-sidebar']:has(
107
+ > [data-composition='grid-item'][data-position='sticky']
108
+ ) {
109
+ --table-progressive-runway-block-size: calc(
110
+ 100vh - var(--nox-site-header-responsive-height) - var(--nox-layout-gap) * 2
111
+ );
112
+ --table-progressive-runway-block-size: calc(
113
+ 100dvh - var(--nox-site-header-responsive-height) - var(--nox-layout-gap) *
114
+ 2
115
+ );
116
+ }
117
+
106
118
  .grid[data-rows] {
107
119
  grid-template-rows: repeat(var(--grid-rows), minmax(0, 1fr));
108
120
  }
@@ -112,6 +124,8 @@
112
124
  [data-layout='fixed'],
113
125
  [data-layout='content-sidebar']
114
126
  )[data-collapse-at='sm'] {
127
+ --table-progressive-runway-block-size: 0px;
128
+
115
129
  grid-template-columns: 1fr;
116
130
  }
117
131
 
@@ -164,6 +178,8 @@
164
178
  [data-layout='fixed'],
165
179
  [data-layout='content-sidebar']
166
180
  )[data-collapse-at='md'] {
181
+ --table-progressive-runway-block-size: 0px;
182
+
167
183
  grid-template-columns: 1fr;
168
184
  }
169
185
 
@@ -216,6 +232,8 @@
216
232
  [data-layout='fixed'],
217
233
  [data-layout='content-sidebar']
218
234
  )[data-collapse-at='lg'] {
235
+ --table-progressive-runway-block-size: 0px;
236
+
219
237
  grid-template-columns: 1fr;
220
238
  }
221
239
 
@@ -268,6 +286,8 @@
268
286
  [data-layout='fixed'],
269
287
  [data-layout='content-sidebar']
270
288
  )[data-collapse-at='xl'] {
289
+ --table-progressive-runway-block-size: 0px;
290
+
271
291
  grid-template-columns: 1fr;
272
292
  }
273
293
 
@@ -44,13 +44,14 @@
44
44
  .grid-item[data-position='sticky'] {
45
45
  position: sticky;
46
46
  z-index: 1;
47
- top: var(--nox-layout-gap);
47
+ top: calc(var(--nox-sticky-viewport-offset) + var(--nox-layout-gap));
48
48
  padding-block-end: var(--nox-border-width);
49
49
  max-block-size: calc(
50
- 100vh - var(--nox-site-header-height) - var(--nox-layout-gap) * 2
50
+ 100vh - var(--nox-site-header-responsive-height) - var(--nox-layout-gap) * 2
51
51
  );
52
52
  max-block-size: calc(
53
- 100dvh - var(--nox-site-header-height) - var(--nox-layout-gap) * 2
53
+ 100dvh - var(--nox-site-header-responsive-height) - var(--nox-layout-gap) *
54
+ 2
54
55
  );
55
56
  align-self: start;
56
57
  overflow-x: hidden;
@@ -29,7 +29,7 @@
29
29
 
30
30
  position: sticky;
31
31
  z-index: 39;
32
- top: 0;
32
+ top: var(--nox-sticky-viewport-offset);
33
33
  display: block;
34
34
  min-width: 0;
35
35
  min-height: var(--surface-group-sticky-occlusion-size);
@@ -6,6 +6,10 @@ const getScrollParent = (element: HTMLElement): HTMLElement | Window => {
6
6
  let parent = element.parentElement;
7
7
 
8
8
  while (parent) {
9
+ if (parent === document.body || parent === document.documentElement) {
10
+ return window;
11
+ }
12
+
9
13
  const styles = window.getComputedStyle(parent);
10
14
 
11
15
  if (/(auto|scroll|overlay)/.test(styles.overflowY)) {
@@ -506,9 +506,8 @@ export function initCombobox(root: HTMLElement): () => void {
506
506
  const siteViewport = document.querySelector<HTMLElement>(
507
507
  '[data-site-viewport]',
508
508
  );
509
- const isSiteScroll = siteViewport
510
- ? target === siteViewport
511
- : target === document || target === window;
509
+ const isSiteScroll =
510
+ target === siteViewport || target === document || target === window;
512
511
  if (isSiteScroll && window.matchMedia(compactViewportQuery).matches) {
513
512
  close();
514
513
  blurFocusedDescendant();
@@ -7,6 +7,8 @@ html:has(.modal[open]) [data-site-viewport] {
7
7
  }
8
8
 
9
9
  .modal {
10
+ --nox-sticky-viewport-offset: 0px;
11
+
10
12
  position: fixed;
11
13
  inset: 0;
12
14
  inline-size: 100%;
@@ -44,13 +44,14 @@
44
44
  .table[data-table-sticky-header] [data-table-header] {
45
45
  position: sticky;
46
46
  z-index: 40;
47
- top: 0;
47
+ top: var(--nox-sticky-viewport-offset);
48
48
  }
49
49
 
50
50
  .table[data-table-sticky-header]:has([data-sticky-surface])
51
51
  [data-table-header] {
52
52
  top: calc(
53
- var(--nox-layout-gap) + var(--surface-group-sticky-surface-block-size)
53
+ var(--nox-sticky-viewport-offset) + var(--nox-layout-gap) +
54
+ var(--surface-group-sticky-surface-block-size)
54
55
  );
55
56
  }
56
57
 
@@ -49,13 +49,19 @@
49
49
  position: relative;
50
50
  display: block;
51
51
  overflow: visible;
52
- block-size: 0;
53
- min-block-size: 0;
52
+ block-size: var(--table-progressive-runway-block-size, 0px);
53
+ min-block-size: var(--table-progressive-runway-block-size, 0px);
54
54
  border: 0;
55
55
  border-radius: 0;
56
56
  background: transparent;
57
57
  }
58
58
 
59
+ .table-pagination[data-table-progressive-enhanced='true']
60
+ .table-pagination__sentinel {
61
+ inset-block-start: 0;
62
+ inset-block-end: auto;
63
+ }
64
+
59
65
  .table-pagination[data-table-progressive-enhanced='true']
60
66
  .table-pagination__content {
61
67
  block-size: 0;
@@ -18,29 +18,57 @@ export function resolveTableStartScrollTop({
18
18
  }
19
19
 
20
20
  export function resolveTableScrollRoot(root: HTMLElement): HTMLElement | null {
21
- return root.closest<HTMLElement>('[data-site-viewport]');
21
+ let candidate = root.parentElement;
22
+
23
+ while (candidate) {
24
+ if (candidate === document.body || candidate === document.documentElement) {
25
+ return null;
26
+ }
27
+
28
+ if (
29
+ /(auto|scroll|overlay)/u.test(
30
+ window.getComputedStyle(candidate).overflowY,
31
+ )
32
+ ) {
33
+ return candidate;
34
+ }
35
+
36
+ candidate = candidate.parentElement;
37
+ }
38
+
39
+ return null;
22
40
  }
23
41
 
24
42
  function resolveTableStickyOffset(root: HTMLElement): number {
25
- const stickySurface = root.querySelector<HTMLElement>(
26
- '[data-sticky-surface]',
27
- );
28
- if (!stickySurface) return 0;
43
+ const stickyElement =
44
+ root.querySelector<HTMLElement>('[data-sticky-surface]') ??
45
+ root.querySelector<HTMLElement>('[data-table-header]');
46
+ if (!stickyElement) return 0;
29
47
 
30
- return Number.parseFloat(window.getComputedStyle(stickySurface).top) || 0;
48
+ return Number.parseFloat(window.getComputedStyle(stickyElement).top) || 0;
31
49
  }
32
50
 
33
51
  export function revealTableStart(root: HTMLElement): void {
34
52
  const scrollRoot = resolveTableScrollRoot(root);
35
- if (!scrollRoot) return;
53
+ const documentScrollRoot = document.scrollingElement;
54
+ if (!scrollRoot && !documentScrollRoot) return;
55
+
56
+ const currentScrollTop = scrollRoot
57
+ ? scrollRoot.scrollTop
58
+ : documentScrollRoot!.scrollTop;
36
59
 
37
60
  const nextScrollTop = resolveTableStartScrollTop({
38
- currentScrollTop: scrollRoot.scrollTop,
61
+ currentScrollTop,
39
62
  stickyOffset: resolveTableStickyOffset(root),
40
63
  tableTop: root.getBoundingClientRect().top,
41
- viewportTop: scrollRoot.getBoundingClientRect().top,
64
+ viewportTop: scrollRoot?.getBoundingClientRect().top ?? 0,
42
65
  });
43
- if (nextScrollTop !== scrollRoot.scrollTop) {
66
+
67
+ if (nextScrollTop === currentScrollTop) return;
68
+
69
+ if (scrollRoot) {
44
70
  scrollRoot.scrollTop = nextScrollTop;
71
+ } else {
72
+ window.scrollTo({ top: nextScrollTop, behavior: 'auto' });
45
73
  }
46
74
  }
@@ -44,46 +44,44 @@
44
44
 
45
45
  html {
46
46
  min-width: 320px;
47
- height: 100%;
48
47
  min-height: 100%;
49
- overflow: hidden;
48
+ overflow-x: hidden;
49
+ overflow-y: auto;
50
+ scroll-padding-block-start: var(--nox-site-header-responsive-height);
51
+ scrollbar-gutter: stable;
50
52
  background: var(--nox-page-background);
51
53
  color: var(--nox-text);
52
54
  font-family: var(--nox-font-body);
53
55
  }
54
56
 
55
57
  body {
56
- height: 100%;
57
58
  min-height: 100%;
58
59
  margin: 0;
59
- overflow: hidden;
60
+ overflow: visible;
60
61
  background: var(--nox-page-background);
61
62
  }
62
63
 
64
+ @media (hover: hover) and (pointer: fine) {
65
+ body {
66
+ border-inline-end: var(--nox-border-width) solid var(--nox-border);
67
+ }
68
+ }
69
+
63
70
  .site-shell {
64
71
  display: grid;
65
- width: 100vw;
66
- height: 100vh;
72
+ width: 100%;
67
73
  min-height: 100vh;
68
74
  grid-template-rows: auto minmax(0, 1fr);
69
- overflow: hidden;
75
+ overflow: visible;
70
76
  background: var(--nox-page-background);
71
77
  }
72
78
 
73
- @supports (height: 100svh) {
79
+ @supports (min-height: 100svh) {
74
80
  .site-shell {
75
- height: 100svh;
76
81
  min-height: 100svh;
77
82
  }
78
83
  }
79
84
 
80
- @media (width >= 48rem) {
81
- .site-shell {
82
- height: 100vh;
83
- min-height: 100vh;
84
- }
85
- }
86
-
87
85
  a {
88
86
  color: var(--nox-content-link-color);
89
87
  font-weight: var(--nox-content-link-font-weight);
@@ -133,13 +131,13 @@ p {
133
131
 
134
132
  .site-viewport {
135
133
  --site-content-gap: var(--nox-space-5);
134
+ --nox-sticky-viewport-offset: var(--nox-site-header-responsive-height);
136
135
 
137
136
  min-width: 0;
138
137
  min-height: 0;
139
- overflow-x: hidden;
140
- overflow-y: auto;
138
+ overflow: visible;
141
139
  background: var(--nox-page-background);
142
- overscroll-behavior-y: contain;
140
+ overscroll-behavior-y: auto;
143
141
  }
144
142
 
145
143
  .site-main {
@@ -215,6 +215,8 @@
215
215
  --nox-navigation-compact-block-size: 3rem;
216
216
  --nox-site-header-height: 4.75rem;
217
217
  --nox-site-header-compact-height: 4rem;
218
+ --nox-site-header-responsive-height: var(--nox-site-header-height);
219
+ --nox-sticky-viewport-offset: 0px;
218
220
 
219
221
  /* Spacing scale. */
220
222
  --nox-space: 0.25rem;
@@ -289,5 +291,6 @@
289
291
  :root {
290
292
  --nox-section-responsive-block-size: var(--nox-section-compact-block-size);
291
293
  --nox-compact-media-responsive-size: 2.125rem;
294
+ --nox-site-header-responsive-height: var(--nox-site-header-compact-height);
292
295
  }
293
296
  }