@pgcorp/ui-kit 0.6.0 → 0.7.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.
Files changed (31) hide show
  1. package/README.md +39 -0
  2. package/docs/public-api.md +6 -0
  3. package/package.json +5 -1
  4. package/src/components/shared/_internal/SToastItem.css +8 -8
  5. package/src/components/shared/_internal/SToastItem.vue +8 -1
  6. package/src/components/shared/controls/SButton.css +6 -6
  7. package/src/components/shared/controls/SButton.vue +9 -1
  8. package/src/components/shared/controls/SInteractiveSurface.css +3 -3
  9. package/src/components/shared/controls/SInteractiveSurface.vue +9 -2
  10. package/src/components/shared/controls/SMarker.css +16 -16
  11. package/src/components/shared/controls/SMarker.vue +27 -1
  12. package/src/components/shared/data-display/SActionListItem.css +2 -2
  13. package/src/components/shared/data-display/SActionListItem.vue +8 -1
  14. package/src/components/shared/data-display/SMessage.css +8 -8
  15. package/src/components/shared/data-display/SMessage.vue +8 -1
  16. package/src/components/shared/data-display/SProgressBar.css +12 -12
  17. package/src/components/shared/data-display/SProgressBar.vue +12 -6
  18. package/src/components/shared/data-display/SProgressIndicator.css +5 -5
  19. package/src/components/shared/data-display/SProgressIndicator.vue +9 -1
  20. package/src/components/shared/data-display/SStatus.css +10 -10
  21. package/src/components/shared/data-display/SStatus.vue +9 -1
  22. package/src/components/shared/data-display/STable.vue +27 -4
  23. package/src/components/shared/data-display/STooltip.css +4 -4
  24. package/src/components/shared/data-display/STooltip.vue +159 -29
  25. package/src/components/shared/data-display/STooltipTarget.css +26 -0
  26. package/src/components/shared/data-display/STooltipTarget.vue +94 -0
  27. package/src/components/shared/navigation/STabs.vue +11 -3
  28. package/src/internal/ownedAttrs.ts +3 -0
  29. package/src/internal/pillSurface.ts +4 -3
  30. package/src/internal/semanticTone.ts +23 -0
  31. package/src/styles/style.css +7 -7
package/README.md CHANGED
@@ -159,6 +159,16 @@ Multi-column sorting uses controlled `v-model:sorts`: the consumer sets
159
159
  adds a descriptor, changes its direction, or removes it; the visible number is
160
160
  the priority. The table never reorders rows on its own.
161
161
 
162
+ Последовательные действия заголовков формируют внутренний intent draft до
163
+ подтверждения controlled prop. Consumer может применить `sort`/`sorts` через
164
+ store или асинхронный render-cycle: второй критерий не теряет первый. Любое
165
+ authoritative обновление prop становится новой базой следующих действий.
166
+
167
+ Sequential header actions update an internal intent draft until the controlled
168
+ prop is acknowledged. Consumers may apply `sort` or `sorts` through a store or
169
+ an asynchronous render cycle without losing an earlier criterion. Any
170
+ authoritative prop update becomes the base for subsequent actions.
171
+
162
172
  ```vue
163
173
  <STable
164
174
  v-model:sorts="sorts"
@@ -170,6 +180,31 @@ the priority. The table never reorders rows on its own.
170
180
  />
171
181
  ```
172
182
 
183
+ ### Подсказка на части действия / Tooltip for part of an action
184
+
185
+ Обычный tooltip использует combined `trigger`. Для пассивного фрагмента внутри
186
+ уже интерактивного leaf используйте split bindings: `focusOwner` остаётся на
187
+ кнопке и владеет ARIA/focus, а `pointerTarget` получает пассивный
188
+ `STooltipTarget`. Компонент target не создаёт `role` или `tabindex`.
189
+
190
+ A regular tooltip uses the combined `trigger`. For a passive fragment inside an
191
+ existing interactive leaf, use split bindings: `focusOwner` remains on the
192
+ button and owns ARIA/focus, while `pointerTarget` is applied to a passive
193
+ `STooltipTarget`. The target component adds neither a role nor a tab stop.
194
+
195
+ ```vue
196
+ <STooltip text="Открывает палитру команд" v-slot="{ focusOwner, pointerTarget }">
197
+ <SButton v-bind="focusOwner" width="full" content-align="between">
198
+ Открыть палитру команд
199
+ <STooltipTarget
200
+ v-bind="pointerTarget"
201
+ as="kbd"
202
+ appearance="keycap"
203
+ >⌘K</STooltipTarget>
204
+ </SButton>
205
+ </STooltip>
206
+ ```
207
+
173
208
  ## Темы и стили / Themes and styles
174
209
 
175
210
  Для обычного приложения подключайте полный style entrypoint один раз:
@@ -244,6 +279,10 @@ import { themeVariants } from '@pgcorp/ui-kit/theme'
244
279
  Do not style internal DOM through `:deep` or private selectors.
245
280
  - Выражайте состояние через documented props, slots, variants и tokens.
246
281
  Express state through documented props, slots, variants, and tokens.
282
+ - Используйте собственные `data-*` как test/tour hooks; UI-kit-owned `data-s-*`
283
+ являются внутренней presentation boundary и не являются selector API.
284
+ Use consumer-owned `data-*` for test and tour hooks; UI-kit-owned `data-s-*`
285
+ are an internal presentation boundary, not a selector API.
247
286
  - Передавайте человекочитаемые labels интерактивным компонентам.
248
287
  Provide human-readable labels to interactive components.
249
288
 
@@ -18,6 +18,9 @@
18
18
 
19
19
  Компонентный contract выражается через typed props, slots, events и exposed
20
20
  methods. Внутренний DOM, CSS-классы и private modules не являются API.
21
+ UI-kit-owned атрибуты `data-s-*` относятся к внутренней presentation boundary и
22
+ не являются стабильными consumer selectors. Произвольные consumer-owned
23
+ `data-*` остаются поддерживаемыми test/tour hooks canonical owner.
21
24
  Публичные source-SFC объявляют runtime props и events локально: consumer Vue
22
25
  compiler регистрирует их без межфайлового filesystem type resolver.
23
26
  Публикуемый production-source компилируется с `target/lib: ES2020`, совпадающим
@@ -41,6 +44,9 @@ Primary groups:
41
44
 
42
45
  Component contracts are expressed through typed props, slots, events, and exposed
43
46
  methods. Internal DOM, CSS classes, and private modules are not API.
47
+ UI-kit-owned `data-s-*` attributes belong to the internal presentation boundary
48
+ and are not stable consumer selectors. Arbitrary consumer-owned `data-*`
49
+ attributes remain supported test and tour hooks on the canonical owner.
44
50
  Public source SFCs declare runtime props and events locally, so the consumer Vue
45
51
  compiler registers them without a cross-file filesystem type resolver.
46
52
  Published production source compiles with the `target/lib: ES2020` baseline from
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgcorp/ui-kit",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
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",
@@ -348,6 +348,9 @@
348
348
  "shared/data-display/STooltipAnchor.vue": [
349
349
  "src/components/shared/data-display/STooltipAnchor.vue"
350
350
  ],
351
+ "shared/data-display/STooltipTarget.vue": [
352
+ "src/components/shared/data-display/STooltipTarget.vue"
353
+ ],
351
354
  "shared/data-display/SVirtualList": [
352
355
  "src/components/shared/data-display/SVirtualList.ts"
353
356
  ],
@@ -524,6 +527,7 @@
524
527
  "./shared/data-display/SToastContainer.vue": "./src/components/shared/data-display/SToastContainer.vue",
525
528
  "./shared/data-display/STooltip.vue": "./src/components/shared/data-display/STooltip.vue",
526
529
  "./shared/data-display/STooltipAnchor.vue": "./src/components/shared/data-display/STooltipAnchor.vue",
530
+ "./shared/data-display/STooltipTarget.vue": "./src/components/shared/data-display/STooltipTarget.vue",
527
531
  "./shared/data-display/SVirtualList": "./src/components/shared/data-display/SVirtualList.ts",
528
532
  "./shared/data-display/SVirtualList.vue": "./src/components/shared/data-display/SVirtualList.vue",
529
533
  "./shared/database/SDataGrid.vue": "./src/components/shared/database/SDataGrid.vue",
@@ -33,33 +33,33 @@
33
33
  }
34
34
 
35
35
  /* Severity: info */
36
- .s-toast[data-severity='info'] {
36
+ .s-toast[data-s-tone='info'] {
37
37
  box-shadow: var(--s-toast-shadow-info);
38
38
  }
39
- .s-toast[data-severity='info'] .s-toast-icon {
39
+ .s-toast[data-s-tone='info'] .s-toast-icon {
40
40
  @apply text-info-500;
41
41
  }
42
42
 
43
43
  /* Severity: success */
44
- .s-toast[data-severity='success'] {
44
+ .s-toast[data-s-tone='success'] {
45
45
  box-shadow: var(--s-toast-shadow-success);
46
46
  }
47
- .s-toast[data-severity='success'] .s-toast-icon {
47
+ .s-toast[data-s-tone='success'] .s-toast-icon {
48
48
  @apply text-success-500;
49
49
  }
50
50
 
51
51
  /* Severity: warn */
52
- .s-toast[data-severity='warn'] {
52
+ .s-toast[data-s-tone='warn'] {
53
53
  box-shadow: var(--s-toast-shadow-warn);
54
54
  }
55
- .s-toast[data-severity='warn'] .s-toast-icon {
55
+ .s-toast[data-s-tone='warn'] .s-toast-icon {
56
56
  @apply text-warn-500;
57
57
  }
58
58
 
59
59
  /* Severity: danger */
60
- .s-toast[data-severity='danger'] {
60
+ .s-toast[data-s-tone='danger'] {
61
61
  box-shadow: var(--s-toast-shadow-danger);
62
62
  }
63
- .s-toast[data-severity='danger'] .s-toast-icon {
63
+ .s-toast[data-s-tone='danger'] .s-toast-icon {
64
64
  @apply text-danger-500;
65
65
  }
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
+ :data-s-tone="toneBinding['data-s-tone']"
3
4
  class="s-toast"
4
- :data-severity="notification.severity"
5
5
  :role="liveAttributes.role"
6
6
  :aria-live="liveAttributes.ariaLive"
7
7
  :aria-atomic="liveAttributes.ariaAtomic"
@@ -40,6 +40,7 @@ import { computed } from 'vue'
40
40
 
41
41
  import { X } from '../../icons/sputnigUiIcons'
42
42
  import { resolveFeedbackLive, resolveFeedbackPresentation } from '../../../internal/feedbackPresentation'
43
+ import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
43
44
  import type { Notification } from '../../../stores/useNotifierStore';
44
45
  import SButton from '../controls/SButton.vue'
45
46
 
@@ -53,6 +54,12 @@ const emit = defineEmits<{
53
54
  dismiss: [id: string]
54
55
  }>()
55
56
  const presentation = computed(() => resolveFeedbackPresentation(props.notification.severity))
57
+ const toneBinding = computed(() => resolveSemanticToneBinding(
58
+ 'SToastItem',
59
+ 'notification.severity',
60
+ props.notification.severity,
61
+ ['info', 'success', 'warn', 'danger'] as const,
62
+ ))
56
63
  const liveAttributes = computed(() => resolveFeedbackLive(props.notification.severity, 'auto'))
57
64
  </script>
58
65
  <style lang="postcss" src="./SToastItem.css" scoped></style>
@@ -78,7 +78,7 @@
78
78
  .s-button[data-icon-only='true'][data-size='md'] { width: var(--s-control-hitbox-md); height: var(--s-control-hitbox-md); }
79
79
  .s-button[data-icon-only='true'][data-size='lg'] { width: var(--s-control-hitbox-lg); height: var(--s-control-hitbox-lg); }
80
80
 
81
- .s-button[data-severity='secondary'] {
81
+ .s-button[data-s-tone='secondary'] {
82
82
  --s-button-accent: var(--s-button-secondary-accent);
83
83
  --s-button-solid-background: var(--s-button-secondary-solid-background);
84
84
  --s-button-solid-background-hover: var(--s-button-secondary-solid-background-hover);
@@ -88,7 +88,7 @@
88
88
  --s-button-outline: var(--color-surface-300);
89
89
  }
90
90
 
91
- .s-button[data-severity='success'] {
91
+ .s-button[data-s-tone='success'] {
92
92
  --s-button-accent: var(--s-button-success-accent);
93
93
  --s-button-solid-background: var(--s-button-success-solid-background);
94
94
  --s-button-solid-background-hover: var(--s-button-success-solid-background-hover);
@@ -97,7 +97,7 @@
97
97
  --s-button-foreground: var(--s-button-success-foreground);
98
98
  }
99
99
 
100
- .s-button[data-severity='info'] {
100
+ .s-button[data-s-tone='info'] {
101
101
  --s-button-accent: var(--s-button-info-accent);
102
102
  --s-button-solid-background: var(--s-button-info-solid-background);
103
103
  --s-button-solid-background-hover: var(--s-button-info-solid-background-hover);
@@ -106,7 +106,7 @@
106
106
  --s-button-foreground: var(--s-button-info-foreground);
107
107
  }
108
108
 
109
- .s-button[data-severity='warn'] {
109
+ .s-button[data-s-tone='warn'] {
110
110
  --s-button-accent: var(--s-button-warn-accent);
111
111
  --s-button-solid-background: var(--s-button-warn-solid-background);
112
112
  --s-button-solid-background-hover: var(--s-button-warn-solid-background-hover);
@@ -115,7 +115,7 @@
115
115
  --s-button-foreground: var(--s-button-warn-foreground);
116
116
  }
117
117
 
118
- .s-button[data-severity='danger'] {
118
+ .s-button[data-s-tone='danger'] {
119
119
  --s-button-accent: var(--s-button-danger-accent);
120
120
  --s-button-solid-background: var(--s-button-danger-solid-background);
121
121
  --s-button-solid-background-hover: var(--s-button-danger-solid-background-hover);
@@ -124,7 +124,7 @@
124
124
  --s-button-foreground: var(--s-button-danger-foreground);
125
125
  }
126
126
 
127
- .s-button[data-severity='contrast'] {
127
+ .s-button[data-s-tone='contrast'] {
128
128
  --s-button-accent: var(--s-button-contrast-accent);
129
129
  --s-button-solid-background: var(--s-button-contrast-solid-background);
130
130
  --s-button-solid-background-hover: var(--s-button-contrast-solid-background-hover);
@@ -3,8 +3,8 @@
3
3
  :is="rootComponent"
4
4
  :ref="setRootRef"
5
5
  v-bind="rootBindings"
6
+ :data-s-tone="toneBinding['data-s-tone']"
6
7
  class="s-button"
7
- :data-severity="severity"
8
8
  :data-size="size"
9
9
  :data-width="width"
10
10
  :data-content-align="contentAlign"
@@ -63,6 +63,7 @@ import { ChevronRight } from '../../icons/sputnigUiIcons'
63
63
  import type { InteractiveElementApi } from '../../../internal/interactiveElement'
64
64
  import { resolveLinkTarget } from '../../../internal/linkTarget'
65
65
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
66
+ import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
66
67
  import {
67
68
  PassiveContentBoundary,
68
69
  useInteractiveLeafRegistration,
@@ -79,6 +80,7 @@ import type {
79
80
  defineOptions({ inheritAttrs: false })
80
81
 
81
82
  export type SButtonSeverity = 'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger' | 'contrast'
83
+ const BUTTON_SEVERITIES = ['primary', 'secondary', 'success', 'info', 'warn', 'danger', 'contrast'] as const satisfies readonly SButtonSeverity[]
82
84
  export type SButtonSize = 'xs' | 'sm' | 'md' | 'lg'
83
85
  export type SButtonIconSize = 'default' | 'large'
84
86
  export type SButtonDensity = 'default' | 'compact'
@@ -186,6 +188,12 @@ const defaultContentComponent = (): ReturnType<NonNullable<typeof buttonSlots.de
186
188
  )
187
189
  const rootRef = ref<HTMLButtonElement | HTMLAnchorElement | null>(null)
188
190
  const ownedAttrs = useOwnedAttrs({ component: 'SButton', owner: 'native button' })
191
+ const toneBinding = computed(() => resolveSemanticToneBinding(
192
+ 'SButton',
193
+ 'severity',
194
+ props.severity,
195
+ BUTTON_SEVERITIES,
196
+ ))
189
197
  const isRegisteredTreeAction = useTreeActionRegistration({
190
198
  owner: 'SButton',
191
199
  element: rootRef,
@@ -51,9 +51,9 @@
51
51
  .s-interactive-surface[data-radius='md'] { @apply rounded-md; }
52
52
  .s-interactive-surface[data-radius='lg'] { @apply rounded-lg; }
53
53
  .s-interactive-surface[data-radius='xl'] { @apply rounded-xl; }
54
- .s-interactive-surface[data-tone='primary'] { @apply border-primary-200 bg-primary-500/5 dark:border-primary-500/30 dark:bg-primary-500/10; }
55
- .s-interactive-surface[data-tone='success'] { @apply border-success-200 bg-success-50 dark:border-success-800 dark:bg-success-950; }
56
- .s-interactive-surface[data-tone='danger'] { @apply border-danger-200 bg-danger-50 dark:border-danger-800 dark:bg-danger-950; }
54
+ .s-interactive-surface[data-s-tone='primary'] { @apply border-primary-200 bg-primary-500/5 dark:border-primary-500/30 dark:bg-primary-500/10; }
55
+ .s-interactive-surface[data-s-tone='success'] { @apply border-success-200 bg-success-50 dark:border-success-800 dark:bg-success-950; }
56
+ .s-interactive-surface[data-s-tone='danger'] { @apply border-danger-200 bg-danger-50 dark:border-danger-800 dark:bg-danger-950; }
57
57
  .s-interactive-surface[data-selected='true'] { @apply border-primary-400 bg-primary-50 dark:border-primary-600 dark:bg-primary-950; }
58
58
  .s-interactive-surface[data-pressed='true'] { @apply border-primary-500 bg-primary-100 dark:border-primary-500 dark:bg-primary-950/70; }
59
59
  .s-interactive-surface[data-disabled='true'] { @apply cursor-not-allowed opacity-60; }
@@ -5,6 +5,7 @@ import type { STooltipTriggerBinding } from '../data-display/STooltip.vue'
5
5
  import { hasOwn } from '../../../internal/es2020'
6
6
  import { resolveLinkTarget } from '../../../internal/linkTarget'
7
7
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
8
+ import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
8
9
  import {
9
10
  PassiveContentBoundary,
10
11
  useInteractiveLeafRegistration,
@@ -14,6 +15,7 @@ import {
14
15
  defineOptions({ inheritAttrs: false })
15
16
 
16
17
  export type InteractiveSurfaceTone = 'default' | 'primary' | 'success' | 'danger'
18
+ const INTERACTIVE_SURFACE_TONES = ['default', 'primary', 'success', 'danger'] as const satisfies readonly InteractiveSurfaceTone[]
17
19
  export type InteractiveSurfaceDensity = 'compact' | 'comfortable'
18
20
  export type InteractiveSurfacePadding = 'none' | 'sm' | 'md'
19
21
  export type InteractiveSurfaceRadius = 'md' | 'lg' | 'xl'
@@ -84,6 +86,12 @@ defineSlots<{
84
86
  }>()
85
87
 
86
88
  const ownedAttrs = useOwnedAttrs({ component: 'SInteractiveSurface', owner: 'interactive surface owner' })
89
+ const toneBinding = computed(() => resolveSemanticToneBinding(
90
+ 'SInteractiveSurface',
91
+ 'tone',
92
+ props.tone,
93
+ INTERACTIVE_SURFACE_TONES,
94
+ ))
87
95
 
88
96
  const assertOptionalBoolean = (surface: Record<string, unknown>, field: 'disabled' | 'pressed'): void => {
89
97
  if (surface[field] !== undefined && typeof surface[field] !== 'boolean') {
@@ -213,7 +221,6 @@ const validatedTooltipTrigger = computed<STooltipTriggerBinding | undefined>(()
213
221
  }
214
222
  return candidate as STooltipTriggerBinding
215
223
  })
216
-
217
224
  const activationBindings = (): Readonly<Record<string, unknown>> => ({
218
225
  ...ownedAttrs.bindings(),
219
226
  ...(validatedTooltipTrigger.value ?? {}),
@@ -282,9 +289,9 @@ function activateAction(event: MouseEvent): void {
282
289
  <template>
283
290
  <div
284
291
  v-bind="surfaceMode === 'static' ? ownedAttrs.bindings() : undefined"
292
+ :data-s-tone="toneBinding['data-s-tone']"
285
293
  class="s-interactive-surface"
286
294
  :data-mode="surfaceMode"
287
- :data-tone="tone"
288
295
  :data-density="density"
289
296
  :data-padding="padding"
290
297
  :data-radius="radius"
@@ -1,19 +1,19 @@
1
1
  @reference "../../../styles/reference.css";
2
2
  .s-marker { @apply inline-block h-2 w-2 shrink-0 rounded-full bg-surface-400 ring-1 ring-inset ring-surface-500/50; }
3
3
  .s-marker[data-size='lg'] { @apply h-7 w-7 ring-2 ring-white/80 shadow-sm dark:ring-surface-950/80; }
4
- .s-marker[data-tone='primary'] { @apply bg-primary-500 ring-primary-600/50; }
5
- .s-marker[data-tone='success'] { @apply bg-success-500 ring-success-600/50; }
6
- .s-marker[data-tone='blue'] { @apply bg-blue-400 ring-blue-500/50; }
7
- .s-marker[data-tone='sky'] { @apply bg-sky-400 ring-sky-500/50; }
8
- .s-marker[data-tone='indigo'] { @apply bg-indigo-400 ring-indigo-500/50; }
9
- .s-marker[data-tone='emerald'] { @apply bg-emerald-400 ring-emerald-500/50; }
10
- .s-marker[data-tone='green'] { @apply bg-green-400 ring-green-500/50; }
11
- .s-marker[data-tone='teal'] { @apply bg-teal-400 ring-teal-500/50; }
12
- .s-marker[data-tone='amber'] { @apply bg-amber-400 ring-amber-500/50; }
13
- .s-marker[data-tone='orange'] { @apply bg-orange-400 ring-orange-500/50; }
14
- .s-marker[data-tone='rose'] { @apply bg-rose-400 ring-rose-500/50; }
15
- .s-marker[data-tone='pink'] { @apply bg-pink-400 ring-pink-500/50; }
16
- .s-marker[data-tone='fuchsia'] { @apply bg-fuchsia-400 ring-fuchsia-500/50; }
17
- .s-marker[data-tone='cyan'] { @apply bg-cyan-400 ring-cyan-500/50; }
18
- .s-marker[data-tone='lime'] { @apply bg-lime-400 ring-lime-500/50; }
19
- .s-marker[data-tone='custom'] { background-color: var(--s-marker-custom-color); --tw-ring-color: var(--s-marker-custom-color); }
4
+ .s-marker[data-s-tone='primary'] { @apply bg-primary-500 ring-primary-600/50; }
5
+ .s-marker[data-s-tone='success'] { @apply bg-success-500 ring-success-600/50; }
6
+ .s-marker[data-s-tone='blue'] { @apply bg-blue-400 ring-blue-500/50; }
7
+ .s-marker[data-s-tone='sky'] { @apply bg-sky-400 ring-sky-500/50; }
8
+ .s-marker[data-s-tone='indigo'] { @apply bg-indigo-400 ring-indigo-500/50; }
9
+ .s-marker[data-s-tone='emerald'] { @apply bg-emerald-400 ring-emerald-500/50; }
10
+ .s-marker[data-s-tone='green'] { @apply bg-green-400 ring-green-500/50; }
11
+ .s-marker[data-s-tone='teal'] { @apply bg-teal-400 ring-teal-500/50; }
12
+ .s-marker[data-s-tone='amber'] { @apply bg-amber-400 ring-amber-500/50; }
13
+ .s-marker[data-s-tone='orange'] { @apply bg-orange-400 ring-orange-500/50; }
14
+ .s-marker[data-s-tone='rose'] { @apply bg-rose-400 ring-rose-500/50; }
15
+ .s-marker[data-s-tone='pink'] { @apply bg-pink-400 ring-pink-500/50; }
16
+ .s-marker[data-s-tone='fuchsia'] { @apply bg-fuchsia-400 ring-fuchsia-500/50; }
17
+ .s-marker[data-s-tone='cyan'] { @apply bg-cyan-400 ring-cyan-500/50; }
18
+ .s-marker[data-s-tone='lime'] { @apply bg-lime-400 ring-lime-500/50; }
19
+ .s-marker[data-s-tone='custom'] { background-color: var(--s-marker-custom-color); --tw-ring-color: var(--s-marker-custom-color); }
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <span
3
3
  v-bind="ownedAttrs.bindings()"
4
+ :data-s-tone="toneBinding['data-s-tone']"
4
5
  class="s-marker"
5
- :data-tone="marker.tone"
6
6
  :data-size="size"
7
7
  :style="customProperties"
8
8
  aria-hidden="true"
@@ -17,12 +17,38 @@ export type { SCustomMarker, SMarker, SMarkerTone } from '../_internal/markerCon
17
17
  <script setup lang="ts">
18
18
  import { computed } from 'vue'
19
19
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
20
+ import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
20
21
  import { createCustomMarker, type SMarker } from '../_internal/markerContract'
21
22
 
22
23
  defineOptions({ inheritAttrs: false })
23
24
 
24
25
  const props = withDefaults(defineProps<{ marker: SMarker; size?: 'sm' | 'lg' }>(), { size: 'sm' })
25
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
46
+ const toneBinding = computed(() => resolveSemanticToneBinding(
47
+ 'SMarker',
48
+ 'marker.tone',
49
+ props.marker.tone,
50
+ MARKER_TONES,
51
+ ))
26
52
  const customProperties = computed(() => {
27
53
  if (props.marker.tone !== 'custom') return undefined
28
54
  createCustomMarker(props.marker.color)
@@ -28,11 +28,11 @@
28
28
  @apply dark:bg-primary-900 dark:text-primary-200;
29
29
  }
30
30
 
31
- .s-action-list-item[data-tone='success'] .s-action-list-item__icon {
31
+ .s-action-list-item[data-s-tone='success'] .s-action-list-item__icon {
32
32
  @apply bg-success-100 text-success-700 dark:bg-success-900 dark:text-success-200;
33
33
  }
34
34
 
35
- .s-action-list-item[data-tone='danger'] .s-action-list-item__icon {
35
+ .s-action-list-item[data-s-tone='danger'] .s-action-list-item__icon {
36
36
  @apply bg-danger-100 text-danger-700 dark:bg-danger-900 dark:text-danger-200;
37
37
  }
38
38
 
@@ -2,6 +2,7 @@
2
2
  import { computed } from 'vue'
3
3
  import type { Component, VNodeChild } from 'vue'
4
4
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
5
+ import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
5
6
  import { validateBoundedInteger, validateNonEmptyString } from '../../../internal/runtimeContract'
6
7
 
7
8
  import SButton from '../controls/SButton.vue'
@@ -102,6 +103,12 @@ const itemStyle = computed(() => ({
102
103
  '--s-action-list-item-indent': `${validatedIndentLevel.value * 0.875}rem`,
103
104
  }))
104
105
  const ownedAttrs = useOwnedAttrs({ component: 'SActionListItem', owner: 'action list item root' })
106
+ const toneBinding = computed(() => resolveSemanticToneBinding(
107
+ 'SActionListItem',
108
+ 'tone',
109
+ props.tone,
110
+ ['default', 'primary', 'success', 'danger'] as const,
111
+ ))
105
112
  const rootBindings = computed(() => ownedAttrs.bindings())
106
113
  const semanticRole = computed(() => {
107
114
  const declaredRole = rootBindings.value.role
@@ -163,9 +170,9 @@ function emitAction(event: MouseEvent): void {
163
170
  <template>
164
171
  <div
165
172
  v-bind="rootBindings"
173
+ :data-s-tone="toneBinding['data-s-tone']"
166
174
  class="s-action-list-item"
167
175
  :data-selected="selected"
168
- :data-tone="tone"
169
176
  :data-density="density"
170
177
  :data-interactive="resolvedMode !== 'static'"
171
178
  :data-mode="resolvedMode"
@@ -52,37 +52,37 @@
52
52
  }
53
53
 
54
54
  /* Severity: info */
55
- .s-message[data-severity='info'] {
55
+ .s-message[data-s-tone='info'] {
56
56
  @apply bg-info-50 border-info-200 text-info-800;
57
57
  @apply dark:bg-info-950 dark:border-info-800 dark:text-info-200;
58
58
  }
59
- .s-message[data-severity='info'] .s-message-icon {
59
+ .s-message[data-s-tone='info'] .s-message-icon {
60
60
  @apply text-info-500 dark:text-info-400;
61
61
  }
62
62
 
63
63
  /* Severity: success */
64
- .s-message[data-severity='success'] {
64
+ .s-message[data-s-tone='success'] {
65
65
  @apply bg-success-50 border-success-200 text-success-800;
66
66
  @apply dark:bg-success-950 dark:border-success-800 dark:text-success-200;
67
67
  }
68
- .s-message[data-severity='success'] .s-message-icon {
68
+ .s-message[data-s-tone='success'] .s-message-icon {
69
69
  @apply text-success-500 dark:text-success-400;
70
70
  }
71
71
 
72
72
  /* Severity: warn */
73
- .s-message[data-severity='warn'] {
73
+ .s-message[data-s-tone='warn'] {
74
74
  @apply bg-warn-50 border-warn-200 text-warn-800;
75
75
  @apply dark:bg-warn-950 dark:border-warn-800 dark:text-warn-200;
76
76
  }
77
- .s-message[data-severity='warn'] .s-message-icon {
77
+ .s-message[data-s-tone='warn'] .s-message-icon {
78
78
  @apply text-warn-500 dark:text-warn-400;
79
79
  }
80
80
 
81
81
  /* Severity: danger */
82
- .s-message[data-severity='danger'] {
82
+ .s-message[data-s-tone='danger'] {
83
83
  @apply bg-danger-50 border-danger-200 text-danger-800;
84
84
  @apply dark:bg-danger-950 dark:border-danger-800 dark:text-danger-200;
85
85
  }
86
- .s-message[data-severity='danger'] .s-message-icon {
86
+ .s-message[data-s-tone='danger'] .s-message-icon {
87
87
  @apply text-danger-500 dark:text-danger-400;
88
88
  }
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <div
3
3
  v-bind="ownedAttrs.bindings()"
4
+ :data-s-tone="toneBinding['data-s-tone']"
4
5
  class="s-message"
5
- :data-severity="validatedSeverity"
6
6
  :data-size="validatedSize"
7
7
  :role="liveAttributes.role"
8
8
  :aria-live="liveAttributes.ariaLive"
@@ -43,6 +43,7 @@
43
43
  import { computed } from 'vue'
44
44
  import { X } from '../../icons/sputnigUiIcons'
45
45
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
46
+ import { resolveSemanticToneBinding } from '../../../internal/semanticTone'
46
47
  import {
47
48
  resolveFeedbackLive,
48
49
  resolveFeedbackPresentation,
@@ -112,6 +113,12 @@ const validatedSeverity = computed<Severity>(() => validateExactString(
112
113
  props.severity,
113
114
  MESSAGE_SEVERITIES,
114
115
  ))
116
+ const toneBinding = computed(() => resolveSemanticToneBinding(
117
+ 'SMessage',
118
+ 'severity',
119
+ validatedSeverity.value,
120
+ MESSAGE_SEVERITIES,
121
+ ))
115
122
  const validatedLive = computed<FeedbackLiveMode>(() => validateExactString(
116
123
  'SMessage',
117
124
  'live',
@@ -22,22 +22,22 @@
22
22
  }
23
23
 
24
24
  /* Severity Colors */
25
- .s-progress-bar-value[data-severity='primary'] {
25
+ .s-progress-bar-value[data-s-tone='primary'] {
26
26
  background-color: var(--s-progress-bar-fill-primary);
27
27
  }
28
- .s-progress-bar-value[data-severity='success'] {
28
+ .s-progress-bar-value[data-s-tone='success'] {
29
29
  background-color: var(--s-progress-bar-fill-success);
30
30
  }
31
- .s-progress-bar-value[data-severity='info'] {
31
+ .s-progress-bar-value[data-s-tone='info'] {
32
32
  background-color: var(--s-progress-bar-fill-info);
33
33
  }
34
- .s-progress-bar-value[data-severity='warn'] {
34
+ .s-progress-bar-value[data-s-tone='warn'] {
35
35
  background-color: var(--s-progress-bar-fill-warn);
36
36
  }
37
- .s-progress-bar-value[data-severity='danger'] {
37
+ .s-progress-bar-value[data-s-tone='danger'] {
38
38
  background-color: var(--s-progress-bar-fill-danger);
39
39
  }
40
- .s-progress-bar-value[data-severity='neutral'] {
40
+ .s-progress-bar-value[data-s-tone='neutral'] {
41
41
  background-color: var(--s-progress-bar-fill-neutral);
42
42
  }
43
43
 
@@ -64,21 +64,21 @@
64
64
  }
65
65
 
66
66
  /* Indeterminate Severity Colors */
67
- .s-progress-bar-value--indeterminate[data-severity='primary']::before {
67
+ .s-progress-bar-value--indeterminate[data-s-tone='primary']::before {
68
68
  background-color: var(--s-progress-bar-fill-primary);
69
69
  }
70
- .s-progress-bar-value--indeterminate[data-severity='success']::before {
70
+ .s-progress-bar-value--indeterminate[data-s-tone='success']::before {
71
71
  background-color: var(--s-progress-bar-fill-success);
72
72
  }
73
- .s-progress-bar-value--indeterminate[data-severity='info']::before {
73
+ .s-progress-bar-value--indeterminate[data-s-tone='info']::before {
74
74
  background-color: var(--s-progress-bar-fill-info);
75
75
  }
76
- .s-progress-bar-value--indeterminate[data-severity='warn']::before {
76
+ .s-progress-bar-value--indeterminate[data-s-tone='warn']::before {
77
77
  background-color: var(--s-progress-bar-fill-warn);
78
78
  }
79
- .s-progress-bar-value--indeterminate[data-severity='danger']::before {
79
+ .s-progress-bar-value--indeterminate[data-s-tone='danger']::before {
80
80
  background-color: var(--s-progress-bar-fill-danger);
81
81
  }
82
- .s-progress-bar-value--indeterminate[data-severity='neutral']::before {
82
+ .s-progress-bar-value--indeterminate[data-s-tone='neutral']::before {
83
83
  background-color: var(--s-progress-bar-indeterminate-neutral-fill);
84
84
  }