@timmo001/groundwork-ui 0.9.14 → 0.9.16

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.
@@ -16675,7 +16675,7 @@
16675
16675
  "text": "readonly CSSStyleSheet[]"
16676
16676
  },
16677
16677
  "static": true,
16678
- "default": "[ base, css` [popover] { position: fixed; box-sizing: border-box; flex-direction: column; padding: 0; margin: 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: 0 solid var(--gw-color-border-normal); box-shadow: var(--gw-shadow-l); transition: translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; translate: 0 0; } /* The page dims as the sheet slides in, and back as it leaves */ [popover]::backdrop { background: rgb(0 0 0 / 0%); transition: background-color var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open::backdrop { background: rgb(0 0 0 / 40%); } @starting-style { [popover]:popover-open::backdrop { background: rgb(0 0 0 / 0%); } } /* Full height from the start or end edge */ [data-side=\"start\"], [data-side=\"end\"] { inset: 0 auto; width: min(var(--gw-sheet-width, 20rem), 100vw); height: 100%; max-height: none; } [data-side=\"start\"] { inset-inline-start: 0; border-inline-end-width: max(var(--gw-border-width), 1px); translate: -100% 0; } [data-side=\"end\"] { inset-inline-end: 0; border-inline-start-width: max(var(--gw-border-width), 1px); translate: 100% 0; } :host(:dir(rtl)) [data-side=\"start\"] { translate: 100% 0; } :host(:dir(rtl)) [data-side=\"end\"] { translate: -100% 0; } /* Up from the bottom, with room for the phone's home bar */ [data-side=\"bottom\"] { inset: auto 0 0; width: 100%; max-width: none; max-height: var(--gw-sheet-height, 85vh); padding-block-end: env(safe-area-inset-bottom); border-block-start-width: max(var(--gw-border-width), 1px); border-start-start-radius: var(--gw-radius-panel); border-start-end-radius: var(--gw-radius-panel); translate: 0 100%; } @starting-style { [data-side=\"start\"]:popover-open { translate: -100% 0; } [data-side=\"end\"]:popover-open { translate: 100% 0; } [data-side=\"bottom\"]:popover-open { translate: 0 100%; } } header { display: flex; flex: none; gap: var(--gw-space-2); align-items: center; padding: var(--gw-space-2) var(--gw-space-3); border-block-end: var(--gw-border-width) solid var(--gw-color-border-quiet); } h2 { flex: 1; min-width: 0; margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .body { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-3); min-height: 0; padding: var(--gw-space-4); /* It scrolls down only, so content reaching into its padding never makes it scroll sideways */ overflow: hidden auto; overscroll-behavior: contain; scrollbar-width: thin; } `, ]",
16678
+ "default": "[ base, css` [popover] { position: fixed; box-sizing: border-box; flex-direction: column; padding: 0; margin: 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: 0 solid var(--gw-color-border-normal); box-shadow: var(--gw-shadow-l); transition: translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; translate: 0 0; } /* The page dims as the sheet slides in, and back as it leaves */ [popover]::backdrop { background: rgb(0 0 0 / 0%); transition: background-color var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open::backdrop { background: rgb(0 0 0 / 40%); } @starting-style { [popover]:popover-open::backdrop { background: rgb(0 0 0 / 0%); } } /* Full height from the start or end edge */ [data-side=\"start\"], [data-side=\"end\"] { inset: 0 auto; width: min(var(--gw-sheet-width, 20rem), 100vw); height: 100%; max-height: none; } [data-side=\"start\"] { inset-inline-start: 0; border-inline-end-width: max(var(--gw-border-width), 1px); translate: -100% 0; } [data-side=\"end\"] { inset-inline-end: 0; border-inline-start-width: max(var(--gw-border-width), 1px); translate: 100% 0; } :host(:dir(rtl)) [data-side=\"start\"] { translate: 100% 0; } :host(:dir(rtl)) [data-side=\"end\"] { translate: -100% 0; } /* Up from the bottom, with room for the phone's home bar */ [data-side=\"bottom\"] { inset: auto 0 0; width: 100%; max-width: none; max-height: var(--gw-sheet-height, 85vh); padding-block-end: env(safe-area-inset-bottom); border-block-start-width: max(var(--gw-border-width), 1px); border-start-start-radius: var(--gw-radius-panel); border-start-end-radius: var(--gw-radius-panel); translate: 0 100%; } @starting-style { [data-side=\"start\"]:popover-open { translate: -100% 0; } [data-side=\"end\"]:popover-open { translate: 100% 0; } [data-side=\"bottom\"]:popover-open { translate: 0 100%; } } header { display: flex; flex: none; gap: var(--gw-space-2); align-items: center; padding: var(--gw-space-2) var(--gw-space-3); border-block-end: var(--gw-border-width) solid var(--gw-color-border-quiet); } h2 { flex: 1; min-width: 0; margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .body { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-3); min-height: 0; padding: var(--gw-space-4); /* It scrolls down only, so content reaching into its padding never makes it scroll sideways */ overflow: hidden auto; overscroll-behavior: contain; } `, ]",
16679
16679
  "inheritedFrom": {
16680
16680
  "name": "GwElement",
16681
16681
  "module": "src/element.ts"
@@ -20762,6 +20762,14 @@
20762
20762
  "module": "src/styles.js"
20763
20763
  }
20764
20764
  },
20765
+ {
20766
+ "kind": "js",
20767
+ "name": "*",
20768
+ "declaration": {
20769
+ "name": "*",
20770
+ "module": "src/scrollbars.js"
20771
+ }
20772
+ },
20765
20773
  {
20766
20774
  "kind": "js",
20767
20775
  "name": "*",
@@ -22839,6 +22847,41 @@
22839
22847
  }
22840
22848
  ]
22841
22849
  },
22850
+ {
22851
+ "kind": "javascript-module",
22852
+ "path": "src/scrollbars.ts",
22853
+ "declarations": [
22854
+ {
22855
+ "kind": "variable",
22856
+ "name": "scrollbars",
22857
+ "default": "new CSSStyleSheet()",
22858
+ "description": "The page's scrollbars for a shadow root: a small rounded thumb on a clear\ntrack with no arrow buttons, squaring off at the ends of its track. Every\n`gw-` element adopts it; add it to an app's own components' styles to match.\nThe `--gw-scrollbar-*` properties restyle it as they do the page's."
22859
+ },
22860
+ {
22861
+ "kind": "function",
22862
+ "name": "followPageScroll",
22863
+ "description": "Sets `--gw-scrollbar-top`, `-bottom`, `-left` and `-right` on the root as\nthe page scrolls, 0 against that end and 1 elsewhere, so the window's\nscrollbar thumb squares off at the ends like the others. The window's\nscrollbar doesn't repaint for the scroll-driven animation the themes give\nother scrollers. Every `gw-` element starts it when it's defined."
22864
+ }
22865
+ ],
22866
+ "exports": [
22867
+ {
22868
+ "kind": "js",
22869
+ "name": "scrollbars",
22870
+ "declaration": {
22871
+ "name": "scrollbars",
22872
+ "module": "src/scrollbars.ts"
22873
+ }
22874
+ },
22875
+ {
22876
+ "kind": "js",
22877
+ "name": "followPageScroll",
22878
+ "declaration": {
22879
+ "name": "followPageScroll",
22880
+ "module": "src/scrollbars.ts"
22881
+ }
22882
+ }
22883
+ ]
22884
+ },
22842
22885
  {
22843
22886
  "kind": "javascript-module",
22844
22887
  "path": "src/styles.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmo001/groundwork-ui",
3
- "version": "0.9.14",
3
+ "version": "0.9.16",
4
4
  "description": "Groundwork's framework-free web components, styled by the --gw-* tokens",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
package/src/element.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  *
6
6
  * @module
7
7
  */
8
+ import { followPageScroll, scrollbars } from "./scrollbars.js";
8
9
 
9
10
  /** How a property reads its attribute and writes it back. */
10
11
  export interface PropertyOptions {
@@ -182,6 +183,8 @@ export class GwElement extends HTMLElement {
182
183
  }
183
184
 
184
185
  if (!customElements.get(tag)) customElements.define(tag, this);
186
+
187
+ followPageScroll();
185
188
  }
186
189
 
187
190
  readonly #values = new Map<string, PropertyValue>();
@@ -224,7 +227,7 @@ export class GwElement extends HTMLElement {
224
227
  if (own.shadow) {
225
228
  const root = this.attachShadow(own.shadowRootOptions);
226
229
 
227
- root.adoptedStyleSheets = [...own.styles];
230
+ root.adoptedStyleSheets = [scrollbars, ...own.styles];
228
231
  this.renderRoot = root;
229
232
  } else {
230
233
  this.renderRoot = this;
package/src/gw-sheet.ts CHANGED
@@ -189,7 +189,6 @@ export class GwSheet extends GwElement {
189
189
  makes it scroll sideways */
190
190
  overflow: hidden auto;
191
191
  overscroll-behavior: contain;
192
- scrollbar-width: thin;
193
192
  }
194
193
  `,
195
194
  ];
package/src/index.ts CHANGED
@@ -9,6 +9,8 @@ export * from "./element.js";
9
9
 
10
10
  export * from "./styles.js";
11
11
 
12
+ export * from "./scrollbars.js";
13
+
12
14
  export * from "./motion.js";
13
15
 
14
16
  export * from "./fields.js";
@@ -0,0 +1,148 @@
1
+ /**
2
+ * The small scrollbars `themes.css` gives the page, as a sheet for shadow
3
+ * roots, which the page's rules can't reach, and the page scroll tracking
4
+ * that squares the window's thumb off at its ends.
5
+ *
6
+ * @module
7
+ */
8
+
9
+ /**
10
+ * The page's scrollbars for a shadow root: a small rounded thumb on a clear
11
+ * track with no arrow buttons, squaring off at the ends of its track. Every
12
+ * `gw-` element adopts it; add it to an app's own components' styles to match.
13
+ * The `--gw-scrollbar-*` properties restyle it as they do the page's.
14
+ */
15
+ export const scrollbars = new CSSStyleSheet();
16
+
17
+ scrollbars.replaceSync(`
18
+ @supports not selector(::-webkit-scrollbar) {
19
+ * {
20
+ scrollbar-width: thin;
21
+ }
22
+ }
23
+
24
+ * {
25
+ animation:
26
+ gw-scrollbar-edges-y linear both,
27
+ gw-scrollbar-edges-x linear both;
28
+ animation-timeline: scroll(self y), scroll(self x);
29
+ }
30
+
31
+ @keyframes gw-scrollbar-edges-y {
32
+ 0% {
33
+ --gw-scrollbar-top: 0;
34
+ }
35
+
36
+ 1% {
37
+ --gw-scrollbar-top: 1;
38
+ }
39
+
40
+ 99% {
41
+ --gw-scrollbar-bottom: 1;
42
+ }
43
+
44
+ 100% {
45
+ --gw-scrollbar-bottom: 0;
46
+ }
47
+ }
48
+
49
+ @keyframes gw-scrollbar-edges-x {
50
+ 0% {
51
+ --gw-scrollbar-left: 0;
52
+ }
53
+
54
+ 1% {
55
+ --gw-scrollbar-left: 1;
56
+ }
57
+
58
+ 99% {
59
+ --gw-scrollbar-right: 1;
60
+ }
61
+
62
+ 100% {
63
+ --gw-scrollbar-right: 0;
64
+ }
65
+ }
66
+
67
+ ::-webkit-scrollbar {
68
+ width: var(--gw-scrollbar-size, 0.4rem);
69
+ height: var(--gw-scrollbar-size, 0.4rem);
70
+ background: var(--gw-scrollbar-track, transparent);
71
+ }
72
+
73
+ ::-webkit-scrollbar-thumb {
74
+ background: var(
75
+ --gw-scrollbar-thumb,
76
+ color-mix(
77
+ in oklab,
78
+ var(--gw-color-text-secondary, currentColor) 45%,
79
+ transparent
80
+ )
81
+ );
82
+ border-radius:
83
+ calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-top, 1))
84
+ calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-top, 1))
85
+ calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-bottom, 1))
86
+ calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-bottom, 1));
87
+ }
88
+
89
+ ::-webkit-scrollbar-thumb:horizontal {
90
+ border-radius:
91
+ calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-left, 1))
92
+ calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-right, 1))
93
+ calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-right, 1))
94
+ calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-left, 1));
95
+ }
96
+
97
+ ::-webkit-scrollbar-thumb:hover {
98
+ background: var(
99
+ --gw-scrollbar-thumb,
100
+ color-mix(
101
+ in oklab,
102
+ var(--gw-color-text-secondary, currentColor) 70%,
103
+ transparent
104
+ )
105
+ );
106
+ }
107
+
108
+ ::-webkit-scrollbar-corner {
109
+ background: transparent;
110
+ }
111
+ `);
112
+
113
+ let followingPageScroll = false;
114
+
115
+ /**
116
+ * Sets `--gw-scrollbar-top`, `-bottom`, `-left` and `-right` on the root as
117
+ * the page scrolls, 0 against that end and 1 elsewhere, so the window's
118
+ * scrollbar thumb squares off at the ends like the others. The window's
119
+ * scrollbar doesn't repaint for the scroll-driven animation the themes give
120
+ * other scrollers. Every `gw-` element starts it when it's defined.
121
+ */
122
+ export const followPageScroll = () => {
123
+ if (followingPageScroll) return;
124
+
125
+ followingPageScroll = true;
126
+
127
+ const root = document.documentElement;
128
+
129
+ const set = (edge: string, against: boolean) => {
130
+ const value = against ? "0" : "1";
131
+
132
+ if (root.style.getPropertyValue(`--gw-scrollbar-${edge}`) !== value)
133
+ root.style.setProperty(`--gw-scrollbar-${edge}`, value);
134
+ };
135
+
136
+ const update = () => {
137
+ const page = document.scrollingElement ?? root;
138
+
139
+ set("top", page.scrollTop <= 0);
140
+ set("bottom", page.scrollTop + page.clientHeight >= page.scrollHeight - 1);
141
+ set("left", page.scrollLeft <= 0);
142
+ set("right", page.scrollLeft + page.clientWidth >= page.scrollWidth - 1);
143
+ };
144
+
145
+ update();
146
+ addEventListener("scroll", update, { passive: true });
147
+ new ResizeObserver(update).observe(root);
148
+ };