@alacris/ui 0.5.1 → 0.6.0

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/README.md CHANGED
@@ -47,9 +47,9 @@ The published package is plain ESM. Point an import map at a pinned CDN build of
47
47
  {
48
48
  "imports": {
49
49
  "@alacris/core": "https://cdn.jsdelivr.net/npm/@alacris/core@0.11.5/dist/alacris.js",
50
- "@alacris/ui": "https://cdn.jsdelivr.net/npm/@alacris/ui@0.5.1/src/index.js",
51
- "@alacris/ui/theme": "https://cdn.jsdelivr.net/npm/@alacris/ui@0.5.1/src/theme/index.js",
52
- "@alacris/ui/components/": "https://cdn.jsdelivr.net/npm/@alacris/ui@0.5.1/src/components/"
50
+ "@alacris/ui": "https://cdn.jsdelivr.net/npm/@alacris/ui@0.6.0/src/index.js",
51
+ "@alacris/ui/theme": "https://cdn.jsdelivr.net/npm/@alacris/ui@0.6.0/src/theme/index.js",
52
+ "@alacris/ui/components/": "https://cdn.jsdelivr.net/npm/@alacris/ui@0.6.0/src/components/"
53
53
  }
54
54
  }
55
55
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alacris/ui",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Themeable design system for Alacris — Material defaults, sixty-nine custom elements, ESM-only, no build step.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,6 +21,79 @@ export const base = css`
21
21
  animation-iteration-count: 1;
22
22
  }
23
23
  }
24
+ @media (hover: hover) and (pointer: fine) {
25
+ :host, *, *::before, *::after {
26
+ scrollbar-width: thin;
27
+ scrollbar-color: ${sys.scrollbar.thumb} ${sys.scrollbar.track};
28
+ }
29
+ ::-webkit-scrollbar {
30
+ inline-size: ${sys.scrollbar.size};
31
+ block-size: ${sys.scrollbar.size};
32
+ }
33
+ ::-webkit-scrollbar-track {
34
+ background: ${sys.scrollbar.track};
35
+ }
36
+ ::-webkit-scrollbar-thumb {
37
+ background-color: ${sys.scrollbar.thumb};
38
+ border-radius: ${sys.scrollbar.radius};
39
+ border: 2px solid transparent;
40
+ background-clip: content-box;
41
+ }
42
+ ::-webkit-scrollbar-thumb:hover {
43
+ background-color: ${sys.scrollbar.thumbHover};
44
+ }
45
+ ::-webkit-scrollbar-thumb:active {
46
+ background-color: ${sys.scrollbar.thumbActive};
47
+ }
48
+ ::-webkit-scrollbar-corner {
49
+ background: transparent;
50
+ }
51
+ ::-webkit-scrollbar-button {
52
+ display: none;
53
+ inline-size: 0;
54
+ block-size: 0;
55
+ }
56
+ }
57
+ `;
58
+
59
+ /**
60
+ * Custom scrollbar rules for a specific selector.
61
+ * Interpolate into a component's css template.
62
+ */
63
+ export const scrollbarOn = (selector) => `
64
+ @media (hover: hover) and (pointer: fine) {
65
+ ${selector} {
66
+ scrollbar-width: thin;
67
+ scrollbar-color: var(--ui-scrollbar-thumb) var(--ui-scrollbar-track);
68
+ }
69
+ ${selector}::-webkit-scrollbar {
70
+ inline-size: var(--ui-scrollbar-size);
71
+ block-size: var(--ui-scrollbar-size);
72
+ }
73
+ ${selector}::-webkit-scrollbar-track {
74
+ background: var(--ui-scrollbar-track);
75
+ }
76
+ ${selector}::-webkit-scrollbar-thumb {
77
+ background-color: var(--ui-scrollbar-thumb);
78
+ border-radius: var(--ui-scrollbar-radius);
79
+ border: 2px solid transparent;
80
+ background-clip: content-box;
81
+ }
82
+ ${selector}::-webkit-scrollbar-thumb:hover {
83
+ background-color: var(--ui-scrollbar-thumb-hover);
84
+ }
85
+ ${selector}::-webkit-scrollbar-thumb:active {
86
+ background-color: var(--ui-scrollbar-thumb-active);
87
+ }
88
+ ${selector}::-webkit-scrollbar-corner {
89
+ background: transparent;
90
+ }
91
+ ${selector}::-webkit-scrollbar-button {
92
+ display: none;
93
+ inline-size: 0;
94
+ block-size: 0;
95
+ }
96
+ }
24
97
  `;
25
98
 
26
99
  /**
@@ -44,3 +117,4 @@ export const stateLayerOn = (host, { focus = host } = {}) => `
44
117
  ${focus}:focus-visible .layer { opacity: var(--ui-state-focus); }
45
118
  ${host}:active .layer { opacity: var(--ui-state-pressed); }
46
119
  `;
120
+
@@ -274,10 +274,25 @@ const styles = css`
274
274
  .overlay {
275
275
  position: fixed;
276
276
  inset: 0;
277
+ inline-size: 100vw;
278
+ block-size: 100vh;
279
+ max-inline-size: 100vw;
280
+ max-block-size: 100vh;
281
+ margin: 0;
282
+ padding: 0;
283
+ border: none;
284
+ background: transparent;
285
+ overflow: visible;
277
286
  z-index: ${sys.z.popup};
278
287
  display: grid;
279
288
  place-items: center;
280
289
  }
290
+ .overlay:popover-open {
291
+ display: grid;
292
+ }
293
+ .overlay::backdrop {
294
+ display: none;
295
+ }
281
296
  .scrim { position: absolute; inset: 0; background: ${t.scrim}; }
282
297
  .modal-surface {
283
298
  position: relative;
@@ -714,8 +729,16 @@ define('ui-date-picker', {
714
729
  releaseFill(animate(el, fx.scaleIn, { duration: 'medium2', easing: 'emphasizedDecelerate' }));
715
730
  };
716
731
 
732
+ const modalOverlayRef = (el) => {
733
+ queueMicrotask(() => {
734
+ try {
735
+ if (el.isConnected) el.showPopover?.();
736
+ } catch {}
737
+ });
738
+ };
739
+
717
740
  const modalView = () => html`
718
- <div class="overlay">
741
+ <div class="overlay" popover="manual" ref=${modalOverlayRef}>
719
742
  <div class="scrim" aria-hidden="true" @click=${closePanel}></div>
720
743
  <div class="modal-surface" part="panel" role="dialog" aria-modal="true"
721
744
  aria-label=${() => label() || 'Choose date'}
@@ -46,10 +46,25 @@ const styles = css`
46
46
  .overlay {
47
47
  position: fixed;
48
48
  inset: 0;
49
+ inline-size: 100vw;
50
+ block-size: 100vh;
51
+ max-inline-size: 100vw;
52
+ max-block-size: 100vh;
53
+ margin: 0;
54
+ padding: 0;
55
+ border: none;
56
+ background: transparent;
57
+ overflow: visible;
49
58
  z-index: ${sys.z.modal};
50
59
  display: grid;
51
60
  place-items: center;
52
61
  }
62
+ .overlay:popover-open {
63
+ display: grid;
64
+ }
65
+ .overlay::backdrop {
66
+ display: none;
67
+ }
53
68
  .scrim { position: absolute; inset: 0; background: ${t.scrim}; }
54
69
  .surface {
55
70
  position: relative;
@@ -146,8 +161,16 @@ define('ui-dialog', {
146
161
  releaseFill(animate(el, fx.scaleIn, { duration: 'medium2', easing: 'emphasizedDecelerate' }));
147
162
  };
148
163
 
164
+ const overlayRef = (el) => {
165
+ queueMicrotask(() => {
166
+ try {
167
+ if (el.isConnected) el.showPopover?.();
168
+ } catch {}
169
+ });
170
+ };
171
+
149
172
  const view = () => html`
150
- <div class="overlay">
173
+ <div class="overlay" popover="manual" ref=${overlayRef}>
151
174
  <div class="scrim" part="scrim" aria-hidden="true" @click=${() => requestClose('scrim')}></div>
152
175
  <div class="surface" part="surface" role="dialog" aria-modal="true"
153
176
  aria-labelledby=${() => (hasHeadline() ? 'headline' : null)}
@@ -47,7 +47,23 @@ const styles = css`
47
47
  .overlay {
48
48
  position: fixed;
49
49
  inset: 0;
50
+ inline-size: 100vw;
51
+ block-size: 100vh;
52
+ max-inline-size: 100vw;
53
+ max-block-size: 100vh;
54
+ margin: 0;
55
+ padding: 0;
56
+ border: none;
57
+ background: transparent;
58
+ overflow: visible;
50
59
  z-index: ${sys.z.drawer};
60
+ display: block;
61
+ }
62
+ .overlay:popover-open {
63
+ display: block;
64
+ }
65
+ .overlay::backdrop {
66
+ display: none;
51
67
  }
52
68
  .scrim { position: absolute; inset: 0; background: ${t.scrim}; }
53
69
  .surface {
@@ -221,8 +237,16 @@ define('ui-drawer', {
221
237
  });
222
238
  };
223
239
 
240
+ const overlayRef = (el) => {
241
+ queueMicrotask(() => {
242
+ try {
243
+ if (el.isConnected) el.showPopover?.();
244
+ } catch {}
245
+ });
246
+ };
247
+
224
248
  const overlay = () => html`
225
- <div class="overlay">
249
+ <div class="overlay" popover="manual" ref=${overlayRef}>
226
250
  <div class="scrim" part="scrim" aria-hidden="true"
227
251
  ref=${(el) => { scrimEl = el; }}
228
252
  @click=${() => requestClose('scrim')}></div>
@@ -48,11 +48,26 @@ const styles = css`
48
48
  .overlay {
49
49
  position: fixed;
50
50
  inset: 0;
51
+ inline-size: 100vw;
52
+ block-size: 100vh;
53
+ max-inline-size: 100vw;
54
+ max-block-size: 100vh;
55
+ margin: 0;
56
+ padding: 0;
57
+ border: none;
58
+ background: transparent;
59
+ overflow: visible;
51
60
  z-index: ${sys.z.modal};
52
61
  display: flex;
53
62
  align-items: flex-end;
54
63
  justify-content: center;
55
64
  }
65
+ .overlay:popover-open {
66
+ display: flex;
67
+ }
68
+ .overlay::backdrop {
69
+ display: none;
70
+ }
56
71
  .scrim { position: absolute; inset: 0; background: ${t.scrim}; }
57
72
  .surface {
58
73
  position: relative;
@@ -241,8 +256,16 @@ define('ui-sheet', {
241
256
  });
242
257
  };
243
258
 
259
+ const overlayRef = (el) => {
260
+ queueMicrotask(() => {
261
+ try {
262
+ if (el.isConnected) el.showPopover?.();
263
+ } catch {}
264
+ });
265
+ };
266
+
244
267
  const modalView = () => html`
245
- <div class="overlay">
268
+ <div class="overlay" popover="manual" ref=${overlayRef}>
246
269
  <div class="scrim" part="scrim" aria-hidden="true"
247
270
  ref=${(el) => { scrimEl = el; }}
248
271
  @click=${() => requestClose('scrim')}></div>
@@ -48,7 +48,23 @@ const styles = css`
48
48
  .overlay {
49
49
  position: fixed;
50
50
  inset: 0;
51
+ inline-size: 100vw;
52
+ block-size: 100vh;
53
+ max-inline-size: 100vw;
54
+ max-block-size: 100vh;
55
+ margin: 0;
56
+ padding: 0;
57
+ border: none;
58
+ background: transparent;
59
+ overflow: visible;
51
60
  z-index: ${sys.z.modal};
61
+ display: block;
62
+ }
63
+ .overlay:popover-open {
64
+ display: block;
65
+ }
66
+ .overlay::backdrop {
67
+ display: none;
52
68
  }
53
69
  .scrim { position: absolute; inset: 0; background: ${t.scrim}; }
54
70
  .surface {
@@ -245,8 +261,16 @@ define('ui-side-sheet', {
245
261
  sync();
246
262
  };
247
263
 
264
+ const overlayRef = (el) => {
265
+ queueMicrotask(() => {
266
+ try {
267
+ if (el.isConnected) el.showPopover?.();
268
+ } catch {}
269
+ });
270
+ };
271
+
248
272
  const modalView = () => html`
249
- <div class="overlay">
273
+ <div class="overlay" popover="manual" ref=${overlayRef}>
250
274
  <div class="scrim" part="scrim" aria-hidden="true"
251
275
  ref=${(el) => { scrimEl = el; }}
252
276
  @click=${() => requestClose('scrim')}></div>
package/src/index.js CHANGED
@@ -36,6 +36,7 @@ export {
36
36
  processTable, filterRows, sortRows, groupRows, paginate,
37
37
  aggregateRow, toCsv, downloadText, inferColumns, visibleColumns,
38
38
  } from './util/table.js';
39
+ export { base, scrollbarOn, focusRingOn, stateLayerOn } from './components/base.js';
39
40
 
40
41
  // Components — importing registers the custom element.
41
42
  import './components/ui-icon.js';
@@ -27,12 +27,49 @@ const block = (tokens) => {
27
27
  // plain typeface. Components then set a type-role shorthand on top.
28
28
  const FACE = 'font-family:var(--ui-font-plain);font-optical-sizing:auto;font-synthesis:none;';
29
29
 
30
+ const SCROLLBAR = [
31
+ '@media (hover: hover) and (pointer: fine) {',
32
+ ' :root, html, body {',
33
+ ' scrollbar-width: thin;',
34
+ ' scrollbar-color: var(--ui-scrollbar-thumb) var(--ui-scrollbar-track);',
35
+ ' }',
36
+ ' ::-webkit-scrollbar {',
37
+ ' inline-size: var(--ui-scrollbar-size);',
38
+ ' block-size: var(--ui-scrollbar-size);',
39
+ ' }',
40
+ ' ::-webkit-scrollbar-track {',
41
+ ' background: var(--ui-scrollbar-track);',
42
+ ' }',
43
+ ' ::-webkit-scrollbar-thumb {',
44
+ ' background-color: var(--ui-scrollbar-thumb);',
45
+ ' border-radius: var(--ui-scrollbar-radius);',
46
+ ' border: 2px solid transparent;',
47
+ ' background-clip: content-box;',
48
+ ' }',
49
+ ' ::-webkit-scrollbar-thumb:hover {',
50
+ ' background-color: var(--ui-scrollbar-thumb-hover);',
51
+ ' }',
52
+ ' ::-webkit-scrollbar-thumb:active {',
53
+ ' background-color: var(--ui-scrollbar-thumb-active);',
54
+ ' }',
55
+ ' ::-webkit-scrollbar-corner {',
56
+ ' background: transparent;',
57
+ ' }',
58
+ ' ::-webkit-scrollbar-button {',
59
+ ' display: none;',
60
+ ' inline-size: 0;',
61
+ ' block-size: 0;',
62
+ ' }',
63
+ '}',
64
+ ].join('\n');
65
+
30
66
  export function themeCss(theme) {
31
67
  const { common, schemes } = theme;
32
68
  return [
33
69
  `:root{${FACE}${block(common)}${block(schemes.light)}}`,
34
70
  `:root[data-ui-scheme="dark"]{${block(schemes.dark)}}`,
35
71
  `@media (prefers-color-scheme: dark){:root:not([data-ui-scheme="light"]){${block(schemes.dark)}}}`,
72
+ SCROLLBAR,
36
73
  ].join('\n');
37
74
  }
38
75
 
@@ -11,7 +11,7 @@ import { makePalettes, makeScheme, COLOR_ROLES } from '../tokens/color.js';
11
11
  import { typographyTokens, resolveTypography } from '../tokens/typography.js';
12
12
  import {
13
13
  shapeTokens, elevationTokens, motionTokens, spacingTokens,
14
- stateTokens, focusTokens, zTokens, densityTokens,
14
+ stateTokens, focusTokens, zTokens, densityTokens, scrollbarTokens,
15
15
  } from '../tokens/system.js';
16
16
 
17
17
  const kebab = (s) => s.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase());
@@ -47,6 +47,7 @@ export function createTheme(config = {}) {
47
47
  ...focusTokens(),
48
48
  ...zTokens(),
49
49
  ...densityTokens(config),
50
+ ...scrollbarTokens(),
50
51
  ...(config.overrides?.common || {}),
51
52
  };
52
53
 
@@ -9,6 +9,7 @@ export {
9
9
  } from './typography.js';
10
10
  export {
11
11
  shapeTokens, elevationTokens, motionTokens, spacingTokens, stateTokens,
12
- focusTokens, zTokens, densityTokens, RADIUS_KEYS, ELEVATION_LEVELS,
12
+ focusTokens, zTokens, densityTokens, scrollbarTokens, RADIUS_KEYS, ELEVATION_LEVELS,
13
13
  DURATIONS, EASINGS, SPACE_STEPS,
14
14
  } from './system.js';
15
+
package/src/tokens/sys.js CHANGED
@@ -69,6 +69,14 @@ export const sys = {
69
69
  snackbar: v('z-snackbar'),
70
70
  tooltip: v('z-tooltip'),
71
71
  },
72
+ scrollbar: {
73
+ thumb: v('scrollbar-thumb'),
74
+ thumbHover: v('scrollbar-thumb-hover'),
75
+ thumbActive: v('scrollbar-thumb-active'),
76
+ track: v('scrollbar-track'),
77
+ size: v('scrollbar-size'),
78
+ radius: v('scrollbar-radius'),
79
+ },
72
80
  density: 'var(--ui-density, 0)',
73
81
  };
74
82
 
@@ -113,3 +113,17 @@ export function zTokens() {
113
113
  export function densityTokens({ density = 0 } = {}) {
114
114
  return { density: String(Math.max(-2, Math.min(0, density))) };
115
115
  }
116
+
117
+ // ---------------------------------------------------------------- scrollbar
118
+
119
+ export function scrollbarTokens() {
120
+ return {
121
+ 'scrollbar-size': '8px',
122
+ 'scrollbar-radius': 'var(--ui-radius-full)',
123
+ 'scrollbar-track': 'transparent',
124
+ 'scrollbar-thumb': 'var(--ui-color-outline-variant)',
125
+ 'scrollbar-thumb-hover': 'var(--ui-color-outline)',
126
+ 'scrollbar-thumb-active': 'var(--ui-color-on-surface-variant)',
127
+ };
128
+ }
129
+
package/types/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- // Hand-written types for @alacris/ui. They track starter/src/index.js and the
2
- // theme / motion / token / util entry points — not generated.
3
- import type { Signal, Computed, Template } from '@alacris/core';
1
+ import type { Signal, Computed, Template, Sheet } from '@alacris/core';
4
2
  import './elements.js';
5
3
 
6
4
  export type Scheme = 'light' | 'dark';
@@ -60,6 +58,7 @@ export const sys: {
60
58
  state: Record<string, string>;
61
59
  focus: Record<string, string>;
62
60
  z: Record<string, string>;
61
+ scrollbar: Record<string, string>;
63
62
  density: string;
64
63
  };
65
64
  export function typeRule(role: string): string;
@@ -190,3 +189,10 @@ export function showSnackbar(
190
189
  message: string,
191
190
  opts?: { action?: string; duration?: number; closeButton?: boolean }
192
191
  ): { close: () => void; closed: Promise<void> };
192
+
193
+ export const base: Sheet;
194
+ export function scrollbarOn(selector: string): string;
195
+ export function focusRingOn(selector: string): string;
196
+ export function stateLayerOn(host: string, opts?: { focus?: string }): string;
197
+ export function scrollbarTokens(): Record<string, string>;
198
+