@pgcorp/ui-kit 0.7.1 → 0.7.3

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
@@ -255,24 +255,31 @@ Components consume semantic custom properties. Consumers override documented
255
255
  tokens on the root theme container and do not depend on internal CSS classes or
256
256
  template structure.
257
257
 
258
- Floating surfaces teleport to `document.body`. If the host application uses an
259
- overlay scale above the UI-kit defaults, align the public layer tokens on
260
- `:root`, preserving `--s-layer-floating < --s-layer-toast <
261
- --s-layer-overlay-backdrop`:
258
+ Floating surfaces teleport to `document.body`; toast, drag fallbacks, modal, and
259
+ drawer also use root-level bands. The values `--s-layer-floating`,
260
+ `--s-layer-toast`, `--s-layer-drag`, and `--s-layer-overlay-backdrop` are base
261
+ anchors. Registered nested layers advance by `--s-layer-overlay-step`, while a
262
+ modal surface uses `--s-layer-overlay-surface-offset` above its backdrop.
263
+
264
+ Integrate a host overlay scale through one root offset:
262
265
 
263
266
  ```css
264
267
  :root {
265
268
  --host-overlay-surface: 2400;
266
- --s-layer-floating: calc(var(--host-overlay-surface) + 10);
267
- --s-layer-toast: calc(var(--host-overlay-surface) + 20);
268
- --s-layer-overlay-backdrop: calc(var(--host-overlay-surface) + 100);
269
+ --s-layer-host-offset: var(--host-overlay-surface);
269
270
  }
270
271
  ```
271
272
 
272
- `--host-overlay-surface` belongs to the host application. The complete layer
273
- integration contract and verification checklist are published in
273
+ `--host-overlay-surface` belongs to the host application. The offset shifts all
274
+ root UI-kit bands together and leaves local content/navigation anchors intact.
275
+ Do not combine it with manual shifts of individual layer tokens. The complete
276
+ owner map, dynamic-stack contract, and verification checklist are published in
274
277
  `docs/theming.md` inside the npm package.
275
278
 
279
+ CodeMirror autocomplete in `SCodeEditor` and the overlay `SResizeHandle` stay
280
+ inside their positioned component owners. They intentionally use the local
281
+ `--s-layer-floating` anchor and are not shifted by `--s-layer-host-offset`.
282
+
276
283
  ## Публичный контракт / Public contract
277
284
 
278
285
  `package.json#exports` — источник истины для импортов. Публичный контракт
package/docs/theming.md CHANGED
@@ -19,32 +19,49 @@ private selectors или зависимость от структуры template
19
19
 
20
20
  ### Интеграция шкалы слоёв host-приложения
21
21
 
22
- Floating surfaces (`STooltip`, меню, select и popover) телепортируются в
23
- `document.body`, поэтому их layer tokens разрешаются в корневом scope документа,
24
- а не во вложенном контейнере компонента. Базовая шкала UI-kit сохраняет порядок
25
- `floating < toast < overlay-backdrop`:
22
+ Floating surfaces (`STooltip`, menu, select, combobox и popover), modal и
23
+ drawer телепортируются в `document.body`. `SToastContainer` также размещается
24
+ в корневой области приложения. Их layer tokens разрешаются на `:root`, а не
25
+ во вложенном theme-контейнере trigger.
26
26
 
27
- - `--s-layer-floating: 40` — tooltip, menu, select и popover;
28
- - `--s-layer-toast: 50` — уведомления;
27
+ Шкала задаёт base anchors, а не статический total order:
28
+
29
+ - `--s-layer-floating: 40` — базовый registered floating layer;
30
+ - `--s-layer-toast: 50` — root-контейнер уведомлений;
31
+ - `--s-layer-drag: 10000` — root drag/fallback surface;
29
32
  - `--s-layer-overlay-backdrop: 11000` — backdrop первого modal/drawer;
30
- - `--s-layer-overlay-surface` вычисляется из backdrop и surface offset.
33
+ - `--s-layer-overlay-surface-offset: 10` surface над её backdrop;
34
+ - `--s-layer-overlay-step: 20` — каждый следующий registered layer в том же
35
+ floating или overlay tier.
36
+
37
+ Поэтому вложенная surface всегда выше своего owner, а базовые 40 и 50 не
38
+ являются обещанием, что любое уведомление выше любой цепочки nested menus.
31
39
 
32
- Если host-приложение использует другую систему overlay-слоёв, задайте UI-kit
33
- tokens на `:root` относительно наибольшего top-level слоя host. Не меняйте
34
- `z-index` внутренних элементов компонентов и не привязывайтесь к их CSS-классам.
40
+ Для интеграции с внешней overlay-шкалой задайте один
41
+ `--s-layer-host-offset` на `:root`. UI-kit применяет его к root-level
42
+ `floating`, `toast`, `drag` и `overlay-backdrop`, сохраняя high-band и
43
+ динамические offsets. Локальные content, sticky, affordance и navigation anchors
44
+ не сдвигаются.
35
45
 
36
46
  ```css
37
47
  :root {
38
48
  --host-overlay-surface: 2400;
39
- --s-layer-floating: calc(var(--host-overlay-surface) + 10);
40
- --s-layer-toast: calc(var(--host-overlay-surface) + 20);
41
- --s-layer-overlay-backdrop: calc(var(--host-overlay-surface) + 100);
49
+ --s-layer-host-offset: var(--host-overlay-surface);
42
50
  }
43
51
  ```
44
52
 
45
- Значение `--host-overlay-surface` в примере является контрактом host-приложения,
46
- а не фиксированным значением UI-kit. После интеграции проверьте tooltip/menu,
47
- toast и modal/drawer внутри и снаружи host overlay.
53
+ `--host-overlay-surface` принадлежит host-приложению. Не комбинируйте
54
+ `--s-layer-host-offset` с ручным сдвигом отдельных UI-kit layer tokens. После
55
+ интеграции проверьте tooltip/menu/select, toast, drag, modal/drawer и floating
56
+ surface внутри modal. Не меняйте `z-index` внутренних элементов компонентов.
57
+
58
+ Локальные floating-элементы не входят в root-level bridge. Autocomplete-tooltip
59
+ `SCodeEditor` остаётся потомком `.cm-editor`, а overlay-вариант
60
+ `SResizeHandle` — абсолютным потомком своего positioned owner. Они используют
61
+ `--s-layer-floating` внутри локального stacking context и не получают
62
+ `--s-layer-host-offset`. Конфигурация CodeMirror с переносом tooltip в
63
+ `document.body` не входит в публичный контракт `SCodeEditor`; root-level surface
64
+ обязана использовать зарегистрированный root layer UI-kit.
48
65
 
49
66
  ## English
50
67
 
@@ -65,29 +82,45 @@ structure dependencies.
65
82
 
66
83
  ### Host application layer-scale integration
67
84
 
68
- Floating surfaces (`STooltip`, menus, selects, and popovers) teleport to
69
- `document.body`. Their layer tokens therefore resolve in the document root scope,
70
- not in a nested component container. The default UI-kit scale preserves
71
- `floating < toast < overlay-backdrop`:
85
+ Floating surfaces (`STooltip`, menus, selects, comboboxes, and popovers), modals,
86
+ and drawers teleport to `document.body`. `SToastContainer` also belongs at the
87
+ application root. Their layer tokens resolve on `:root`, not in the trigger's
88
+ nested theme container.
89
+
90
+ The scale defines base anchors, not a static total order:
91
+
92
+ - `--s-layer-floating: 40` — first registered floating layer;
93
+ - `--s-layer-toast: 50` — root notification container;
94
+ - `--s-layer-drag: 10000` — root drag/fallback surface;
95
+ - `--s-layer-overlay-backdrop: 11000` — first modal/drawer backdrop;
96
+ - `--s-layer-overlay-surface-offset: 10` — surface above its backdrop;
97
+ - `--s-layer-overlay-step: 20` — each subsequent registered layer in the same
98
+ floating or overlay tier.
72
99
 
73
- - `--s-layer-floating: 40` tooltips, menus, selects, and popovers;
74
- - `--s-layer-toast: 50` notifications;
75
- - `--s-layer-overlay-backdrop: 11000` — the first modal/drawer backdrop;
76
- - `--s-layer-overlay-surface` is derived from the backdrop and surface offset.
100
+ A nested surface therefore stays above its owner. The base values 40 and 50 do
101
+ not promise that every notification is above every chain of nested menus.
77
102
 
78
- When the host application uses another overlay scale, define the UI-kit tokens
79
- on `:root` relative to the highest top-level host layer. Do not change component
80
- internal `z-index` values or depend on internal CSS classes.
103
+ To integrate another overlay scale, define one `--s-layer-host-offset` on
104
+ `:root`. UI-kit applies it to the root-level floating, toast, drag, and overlay
105
+ backdrop bands while preserving the high band and dynamic offsets. Local content,
106
+ sticky, affordance, and navigation anchors are not shifted.
81
107
 
82
108
  ```css
83
109
  :root {
84
110
  --host-overlay-surface: 2400;
85
- --s-layer-floating: calc(var(--host-overlay-surface) + 10);
86
- --s-layer-toast: calc(var(--host-overlay-surface) + 20);
87
- --s-layer-overlay-backdrop: calc(var(--host-overlay-surface) + 100);
111
+ --s-layer-host-offset: var(--host-overlay-surface);
88
112
  }
89
113
  ```
90
114
 
91
- The `--host-overlay-surface` value is a host-application contract, not a fixed
92
- UI-kit value. Verify tooltip/menu, toast, and modal/drawer behavior both inside
93
- and outside host overlays after integration.
115
+ `--host-overlay-surface` belongs to the host application. Do not combine
116
+ `--s-layer-host-offset` with manual shifts of individual UI-kit layer tokens.
117
+ Verify tooltip/menu/select, toast, drag, modal/drawer, and a floating surface
118
+ inside a modal. Do not override internal component `z-index` values.
119
+
120
+ Local floating elements are outside the root-level bridge. The `SCodeEditor`
121
+ autocomplete tooltip remains a descendant of `.cm-editor`, and the overlay
122
+ `SResizeHandle` remains an absolutely positioned child of its positioned owner.
123
+ They use `--s-layer-floating` inside a local stacking context and do not receive
124
+ `--s-layer-host-offset`. A CodeMirror configuration that reparents tooltips into
125
+ `document.body` is outside the public `SCodeEditor` contract; a root-level
126
+ surface must use a registered UI-kit root layer.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgcorp/ui-kit",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Typed Vue 3 design system with accessible components, semantic themes, and workbench patterns.",
5
5
  "type": "module",
6
6
  "types": "./index.ts",
@@ -571,7 +571,7 @@
571
571
  "@uiw/codemirror-extensions-langs": "^4.25.8",
572
572
  "codemirror": "6.0.2",
573
573
  "codemirror-lang-makefile": "^0.1.1",
574
- "dompurify": "^3.4.12",
574
+ "dompurify": "^3.4.13",
575
575
  "graphology": "^0.26.0",
576
576
  "highlight.js": "^11.11.1",
577
577
  "@lucide/vue": "^1.16.0",
@@ -598,7 +598,7 @@
598
598
  "eslint-plugin-vue": "^10.8.0",
599
599
  "jsdom": "^28.1.0",
600
600
  "pinia": "^3.0.4",
601
- "postcss": "8.5.19",
601
+ "postcss": "8.5.26",
602
602
  "postcss-selector-parser": "7.1.4",
603
603
  "storybook": "10.5.3",
604
604
  "typescript": "^5.9.3",
@@ -1,4 +1,23 @@
1
- export type SMarkerTone = 'surface' | 'primary' | 'success' | 'blue' | 'sky' | 'indigo' | 'emerald' | 'green' | 'teal' | 'amber' | 'orange' | 'rose' | 'pink' | 'fuchsia' | 'cyan' | 'lime'
1
+ export const S_MARKER_TONES = [
2
+ 'surface',
3
+ 'primary',
4
+ 'success',
5
+ 'blue',
6
+ 'sky',
7
+ 'indigo',
8
+ 'emerald',
9
+ 'green',
10
+ 'teal',
11
+ 'amber',
12
+ 'orange',
13
+ 'rose',
14
+ 'pink',
15
+ 'fuchsia',
16
+ 'cyan',
17
+ 'lime',
18
+ ] as const
19
+
20
+ export type SMarkerTone = typeof S_MARKER_TONES[number]
2
21
  export type SMarker = { tone: SMarkerTone } | { tone: 'custom'; color: `#${string}` }
3
22
  export type SCustomMarker = Extract<SMarker, { tone: 'custom' }>
4
23
 
@@ -68,7 +68,7 @@
68
68
  .sortable-drag.s-cloud-chrome-row,
69
69
  .sortable-fallback.s-cloud-chrome-row {
70
70
  pointer-events: none;
71
- z-index: var(--s-layer-drag);
71
+ z-index: var(--s-layer-root-drag);
72
72
  opacity: 0.96;
73
73
  transform: scale(1.01);
74
74
  box-shadow: var(--s-cloud-chrome-drag-shadow);
@@ -188,7 +188,7 @@ const isOpen = computed(() => props.open ?? uncontrolledOpen.value);
188
188
  useInteractiveLeafRegistration({ owner: 'SPopover', active: () => isOpen.value });
189
189
  const panelId = `s-popover-panel-${useId()}`;
190
190
  const activeTriggerId = ref<string | null>(null);
191
- const layerZIndex = ref('var(--s-layer-floating)');
191
+ const layerZIndex = ref('var(--s-layer-root-floating)');
192
192
  const resolvedTriggerAriaLabel = computed(() =>
193
193
  props.triggerAriaLabel ?? (
194
194
  props.triggerIconOnly || (!props.triggerLabel && !slots.default)
@@ -426,7 +426,7 @@ function deactivateOpenState(reason: CloseReason): void {
426
426
  const shouldRestoreFocus = reason === 'explicit' || reason === 'escape' || reason === 'trigger';
427
427
  layerRegistration?.unregister({ restoreFocus: shouldRestoreFocus });
428
428
  layerRegistration = null;
429
- layerZIndex.value = 'var(--s-layer-floating)';
429
+ layerZIndex.value = 'var(--s-layer-root-floating)';
430
430
  }
431
431
 
432
432
  function assertControlMode(): void {
@@ -147,7 +147,7 @@ const listboxRef = shallowRef<{
147
147
  } | null>(null)
148
148
  const isOpen = shallowRef(false)
149
149
  const activeValue = shallowRef<TKey | null>(null)
150
- const layerZIndex = shallowRef('var(--s-layer-floating)')
150
+ const layerZIndex = shallowRef('var(--s-layer-root-floating)')
151
151
  let layerRegistration: LayerRegistration | null = null
152
152
 
153
153
  const inputId = computed(() => props.id === undefined
@@ -285,7 +285,7 @@ function close(reason: CloseReason): void {
285
285
  activeValue.value = null
286
286
  layerRegistration?.unregister({ restoreFocus: false })
287
287
  layerRegistration = null
288
- layerZIndex.value = 'var(--s-layer-floating)'
288
+ layerZIndex.value = 'var(--s-layer-root-floating)'
289
289
  }
290
290
 
291
291
  async function open(): Promise<void> {
@@ -18,31 +18,13 @@ export type { SCustomMarker, SMarker, SMarkerTone } from '../_internal/markerCon
18
18
  import { computed } from 'vue'
19
19
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
20
20
  import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
21
- import { createCustomMarker, type SMarker } from '../_internal/markerContract'
21
+ import { createCustomMarker, S_MARKER_TONES, type SMarker } from '../_internal/markerContract'
22
22
 
23
23
  defineOptions({ inheritAttrs: false })
24
24
 
25
25
  const props = withDefaults(defineProps<{ marker: SMarker; size?: 'sm' | 'lg' }>(), { size: 'sm' })
26
26
  const ownedAttrs = useOwnedAttrs({ component: 'SMarker', owner: 'marker root' })
27
- const MARKER_TONES = [
28
- 'surface',
29
- 'primary',
30
- 'success',
31
- 'blue',
32
- 'sky',
33
- 'indigo',
34
- 'emerald',
35
- 'green',
36
- 'teal',
37
- 'amber',
38
- 'orange',
39
- 'rose',
40
- 'pink',
41
- 'fuchsia',
42
- 'cyan',
43
- 'lime',
44
- 'custom',
45
- ] as const
27
+ const MARKER_TONES = [...S_MARKER_TONES, 'custom'] as const
46
28
  const toneBinding = computed(() => resolveSemanticToneBinding(
47
29
  'SMarker',
48
30
  'marker.tone',
@@ -248,7 +248,7 @@ const listboxProps = computed<
248
248
  })
249
249
 
250
250
  const isOpen = ref(false)
251
- const layerZIndex = ref('var(--s-layer-floating)')
251
+ const layerZIndex = ref('var(--s-layer-root-floating)')
252
252
  let layerRegistration: LayerRegistration | null = null
253
253
  const triggerRef = ref<SComboboxTriggerApi | null>(null)
254
254
  const listboxRef = ref<{ focusSelectedOrFirst: () => void } | null>(null)
@@ -271,7 +271,7 @@ function close(reason: CloseReason = 'trigger'): void {
271
271
  restoreFocus: reason === 'trigger' || reason === 'selection' || reason === 'escape',
272
272
  })
273
273
  layerRegistration = null
274
- layerZIndex.value = 'var(--s-layer-floating)'
274
+ layerZIndex.value = 'var(--s-layer-root-floating)'
275
275
  }
276
276
 
277
277
  function triggerElement(): HTMLElement {
@@ -2,7 +2,7 @@
2
2
 
3
3
  .s-toast-container {
4
4
  @apply fixed top-0 right-0 flex max-h-full min-w-0 w-full max-w-md flex-col overflow-y-auto overscroll-contain p-4;
5
- z-index: var(--s-layer-toast);
5
+ z-index: var(--s-layer-root-toast);
6
6
  @apply pointer-events-none;
7
7
  }
8
8
 
@@ -190,7 +190,7 @@ const triggerContractError = (message: string): Error => new Error(`STooltip: ${
190
190
 
191
191
  const isOpen = ref(false);
192
192
  const ownedAttrs = useOwnedAttrs({ component: 'STooltip', owner: 'renderless tooltip contract' });
193
- const layerZIndex = ref('var(--s-layer-floating)');
193
+ const layerZIndex = ref('var(--s-layer-root-floating)');
194
194
  let layerRegistration: LayerRegistration | null = null;
195
195
  let hideTimer: number | null = null;
196
196
  let showTimer: number | null = null;
@@ -374,7 +374,7 @@ const closeImmediately = () => {
374
374
  isOpen.value = false;
375
375
  layerRegistration?.unregister({ restoreFocus: false });
376
376
  layerRegistration = null;
377
- layerZIndex.value = 'var(--s-layer-floating)';
377
+ layerZIndex.value = 'var(--s-layer-root-floating)';
378
378
  };
379
379
 
380
380
  const openImmediately = () => {
@@ -157,7 +157,7 @@ export function useFloatingPosition(options: FloatingPositionOptions): FloatingP
157
157
  position: 'fixed',
158
158
  left: `${resolvedCoordinates.value.left}px`,
159
159
  top: `${resolvedCoordinates.value.top}px`,
160
- zIndex: options.zIndex?.() ?? 'var(--s-layer-floating)',
160
+ zIndex: options.zIndex?.() ?? 'var(--s-layer-root-floating)',
161
161
  ...(widthMatch === 'min' ? { minWidth: `${Math.round(anchor.width)}px` } : {}),
162
162
  ...(widthMatch === 'exact' ? { width: `${Math.round(anchor.width)}px` } : {}),
163
163
  }
@@ -218,7 +218,7 @@ export function useFloatingPosition(options: FloatingPositionOptions): FloatingP
218
218
  position: 'fixed',
219
219
  left: '-10000px',
220
220
  top: '-10000px',
221
- zIndex: options.zIndex?.() ?? 'var(--s-layer-floating)',
221
+ zIndex: options.zIndex?.() ?? 'var(--s-layer-root-floating)',
222
222
  }
223
223
  await nextTick()
224
224
  attachListeners()
@@ -162,8 +162,8 @@ export function registerLayer(options: LayerRegistrationOptions): LayerRegistrat
162
162
  const zIndex = previousTopmost && previousTopmost.tier === tier
163
163
  ? `calc(${previousTopmost.zIndex} + var(--s-layer-overlay-step))`
164
164
  : tier === 'overlay'
165
- ? 'var(--s-layer-overlay-backdrop)'
166
- : 'var(--s-layer-floating)'
165
+ ? 'var(--s-layer-root-overlay-backdrop)'
166
+ : 'var(--s-layer-root-floating)'
167
167
  const entry: LayerEntry = {
168
168
  ...options,
169
169
  id: Symbol(options.kind),
@@ -29,7 +29,7 @@ export function useMenuLayer(
29
29
  options: { onDismiss?: (reason: 'escape' | 'outside') => void } = {},
30
30
  ) {
31
31
  const isOpen = ref(false)
32
- const zIndex = ref('var(--s-layer-floating)')
32
+ const zIndex = ref('var(--s-layer-root-floating)')
33
33
  let anchor: MenuAnchor | null = null
34
34
  let focusAnchor: HTMLElement | null = null
35
35
  let registration: LayerRegistration | null = null
@@ -41,7 +41,7 @@ export function useMenuLayer(
41
41
  registration = null
42
42
  anchor = null
43
43
  focusAnchor = null
44
- zIndex.value = 'var(--s-layer-floating)'
44
+ zIndex.value = 'var(--s-layer-root-floating)'
45
45
  }
46
46
 
47
47
  function closeForTab(event: KeyboardEvent): void {
@@ -324,15 +324,20 @@
324
324
  --s-layer-sticky: 10;
325
325
  --s-layer-affordance: 20;
326
326
  --s-layer-status: 25;
327
+ --s-layer-host-offset: 0;
327
328
  --s-layer-floating: 40;
328
329
  --s-layer-toast: 50;
329
330
  --s-layer-navigation: 50;
330
331
  --s-layer-navigation-submenu: 49;
331
332
  --s-layer-drag: 10000;
332
333
  --s-layer-overlay-backdrop: 11000;
334
+ --s-layer-root-floating: calc(var(--s-layer-host-offset) + var(--s-layer-floating));
335
+ --s-layer-root-toast: calc(var(--s-layer-host-offset) + var(--s-layer-toast));
336
+ --s-layer-root-drag: calc(var(--s-layer-host-offset) + var(--s-layer-drag));
337
+ --s-layer-root-overlay-backdrop: calc(var(--s-layer-host-offset) + var(--s-layer-overlay-backdrop));
333
338
  --s-layer-overlay-surface-offset: 10;
334
339
  --s-layer-overlay-step: 20;
335
- --s-layer-overlay-surface: calc(var(--s-layer-overlay-backdrop) + var(--s-layer-overlay-surface-offset));
340
+ --s-layer-overlay-surface: calc(var(--s-layer-root-overlay-backdrop) + var(--s-layer-overlay-surface-offset));
336
341
  --s-overlay-backdrop-blur: 2px;
337
342
  --s-elevation-control: var(--shadow-sm);
338
343
  --s-elevation-floating: var(--shadow-lg);