@pgcorp/ui-kit 0.1.2 → 0.3.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 (45) hide show
  1. package/README.md +5 -5
  2. package/package.json +19 -3
  3. package/src/components/layout/SSideMenuButton.vue +5 -4
  4. package/src/components/layout/SSideMenuRail.vue +3 -6
  5. package/src/components/layout/SStack.css +9 -3
  6. package/src/components/layout/SStack.vue +10 -2
  7. package/src/components/shared/_internal/STreeNode.vue +10 -4
  8. package/src/components/shared/_internal/sidebarGroupContext.ts +3 -1
  9. package/src/components/shared/_internal/tabsContext.ts +1 -0
  10. package/src/components/shared/containers/SLeftSidebar.vue +2 -2
  11. package/src/components/shared/containers/SPageHeader.css +11 -0
  12. package/src/components/shared/containers/SPageHeader.vue +7 -1
  13. package/src/components/shared/containers/SPanel.css +7 -1
  14. package/src/components/shared/containers/SPanel.vue +5 -1
  15. package/src/components/shared/containers/SSidebarGroup.css +1 -1
  16. package/src/components/shared/containers/SSidebarGroup.vue +113 -94
  17. package/src/components/shared/containers/SSidebarSection.css +16 -4
  18. package/src/components/shared/containers/SSidebarSection.vue +15 -16
  19. package/src/components/shared/controls/SButton.css +39 -0
  20. package/src/components/shared/controls/SButton.vue +146 -29
  21. package/src/components/shared/controls/SCombobox.css +9 -0
  22. package/src/components/shared/controls/SCombobox.vue +322 -0
  23. package/src/components/shared/controls/SContextToggleButton.vue +1 -1
  24. package/src/components/shared/controls/SInputText.vue +15 -0
  25. package/src/components/shared/controls/SInteractiveSurface.css +4 -0
  26. package/src/components/shared/controls/SInteractiveSurface.vue +4 -0
  27. package/src/components/shared/controls/SListbox.vue +99 -1
  28. package/src/components/shared/controls/SListboxOption.vue +15 -2
  29. package/src/components/shared/controls/SRange.css +35 -0
  30. package/src/components/shared/controls/SRange.vue +129 -0
  31. package/src/components/shared/controls/SSwitch.css +48 -0
  32. package/src/components/shared/controls/SSwitch.vue +109 -0
  33. package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +6 -3
  34. package/src/components/shared/data-display/SChip.css +59 -0
  35. package/src/components/shared/data-display/SChip.vue +92 -0
  36. package/src/components/shared/data-display/SCodeBlock.vue +1 -1
  37. package/src/components/shared/data-display/SCodeEditor.css +15 -0
  38. package/src/components/shared/data-display/SCodeEditor.vue +14 -0
  39. package/src/components/shared/data-display/STable.vue +107 -3
  40. package/src/components/shared/data-display/table.ts +9 -0
  41. package/src/components/shared/navigation/SBottomNav.vue +3 -0
  42. package/src/components/shared/navigation/STab.vue +31 -6
  43. package/src/components/shared/navigation/STabs.vue +8 -2
  44. package/src/internal/codeEditorContract.ts +1 -1
  45. package/src/internal/ownedAttrs.ts +31 -2
@@ -43,9 +43,10 @@ const emit = defineEmits<SidebarGroupEmits>();
43
43
  const ownedAttrs = useOwnedAttrs({ component: 'SSidebarGroup', owner: 'sidebar group root' });
44
44
  const groupRef = ref<HTMLDivElement | null>(null);
45
45
  const registrations = shallowRef<readonly SidebarSectionRegistration[]>([]);
46
+ const effectiveSizes = new Map<string, number>();
46
47
  let resizeObserver: ResizeObserver | null = null;
47
48
  let mounted = false;
48
- let geometryRevision = 0;
49
+ const geometryRevision = ref(0);
49
50
  let activeResizePair: Readonly<{ topKey: string; bottomKey: string }> | null = null;
50
51
 
51
52
  assertSidebarSectionsState(props.sectionsState);
@@ -122,65 +123,74 @@ function emitTransition(replacements: readonly SidebarSectionReplacement[]): voi
122
123
  }
123
124
 
124
125
  function scheduleGeometryReconciliation(): void {
125
- const revision = ++geometryRevision;
126
+ const revision = ++geometryRevision.value;
126
127
  queueMicrotask(() => {
127
- if (!mounted || revision !== geometryRevision || activeResizePair !== null) return;
128
+ if (!mounted || revision !== geometryRevision.value || activeResizePair !== null) return;
128
129
  reconcileContainerGeometry();
129
130
  });
130
131
  }
131
132
 
133
+ function fitOpenSectionSizes(
134
+ openSections: readonly RegisteredSidebarSection[],
135
+ availableSize: number,
136
+ ): ReadonlyMap<string, number> {
137
+ const minimumTotal = openSections.reduce((sum, section) => sum + section.state.minSize, 0);
138
+ const targetSize = Math.max(minimumTotal, availableSize);
139
+ const flexible = openSections.find((section) => section.state.size === null);
140
+ const sizes = new Map<string, number>();
141
+
142
+ for (const section of openSections) {
143
+ const remembered = effectiveSizes.get(section.key);
144
+ const preferred = remembered ?? section.state.size ?? section.state.minSize;
145
+ sizes.set(section.key, Math.max(section.state.minSize, preferred));
146
+ }
147
+
148
+ let delta = targetSize - [...sizes.values()].reduce((sum, size) => sum + size, 0);
149
+ if (delta > 0) {
150
+ const growTarget = flexible ?? openSections.at(-1);
151
+ if (growTarget) sizes.set(growTarget.key, sizes.get(growTarget.key)! + delta);
152
+ delta = 0;
153
+ }
154
+
155
+ if (delta < 0) {
156
+ const flexibleIndex = flexible
157
+ ? openSections.findIndex((section) => section.key === flexible.key)
158
+ : openSections.length - 1;
159
+ const shrinkOrder = openSections
160
+ .map((section, index) => ({ section, distance: Math.abs(index - flexibleIndex) }))
161
+ .sort((left, right) => left.distance - right.distance);
162
+ let overflow = -delta;
163
+ for (const { section } of shrinkOrder) {
164
+ if (overflow <= 0) break;
165
+ const size = sizes.get(section.key)!;
166
+ const reduction = Math.min(size - section.state.minSize, overflow);
167
+ sizes.set(section.key, size - reduction);
168
+ overflow -= reduction;
169
+ }
170
+ }
171
+
172
+ return sizes;
173
+ }
174
+
132
175
  function reconcileContainerGeometry(): void {
133
176
  const containerHeight = measuredContainerHeight();
134
177
  if (containerHeight === 0) return;
135
178
  const sections = orderedSections.value;
136
179
  const openSections = sections.filter((section) => section.state.isOpen);
137
180
  if (openSections.length === 0) return;
138
- const flexibleIndex = openSections.findIndex((section) => section.state.size === null);
139
- if (flexibleIndex < 0) {
140
- throw new Error('SSidebarGroup: controlled state has no flexible open section');
141
- }
142
-
143
181
  const closedHeight = sections
144
182
  .filter((section) => !section.state.isOpen)
145
183
  .reduce((sum, section) => (
146
184
  sum + measuredHeight(section.element, `sections.${section.key}`)
147
185
  ), 0);
148
- const fixedSections = openSections.filter((_, index) => index !== flexibleIndex);
149
- const fixedHeight = fixedSections.reduce((sum, section) => {
150
- if (section.state.size === null) {
151
- throw new Error(`SSidebarGroup: section '${section.key}' unexpectedly owns flexible size`);
152
- }
153
- return sum + section.state.size;
154
- }, 0);
155
- const flexible = openSections[flexibleIndex]!;
156
- let overflow = closedHeight + fixedHeight + flexible.state.minSize - containerHeight;
157
- if (overflow <= 0) return;
158
-
159
- const orderedCandidates = openSections
160
- .map((section, index) => ({ section, index }))
161
- .filter(({ index }) => index !== flexibleIndex)
162
- .map(({ section, index }) => ({
163
- section,
164
- distance: Math.abs(index - flexibleIndex),
165
- }))
166
- .sort((left, right) => left.distance - right.distance);
167
- const replacements: SidebarSectionReplacement[] = [];
168
- for (const candidate of orderedCandidates) {
169
- if (overflow <= 0) break;
170
- const size = candidate.section.state.size;
171
- if (size === null) {
172
- throw new Error(`SSidebarGroup: section '${candidate.section.key}' unexpectedly owns flexible size`);
173
- }
174
- const available = size - candidate.section.state.minSize;
175
- if (available <= 0) continue;
176
- const reduction = Math.min(available, overflow);
177
- replacements.push({
178
- key: candidate.section.key,
179
- state: { ...candidate.section.state, size: size - reduction },
180
- });
181
- overflow -= reduction;
186
+ const nextSizes = fitOpenSectionSizes(openSections, containerHeight - closedHeight);
187
+ let changed = false;
188
+ for (const section of openSections) {
189
+ const nextSize = nextSizes.get(section.key)!;
190
+ if (effectiveSizes.get(section.key) !== nextSize) changed = true;
191
+ effectiveSizes.set(section.key, nextSize);
182
192
  }
183
- if (replacements.length > 0) emitTransition(replacements);
193
+ if (changed) geometryRevision.value += 1;
184
194
  }
185
195
 
186
196
  function registerSection(registration: SidebarSectionRegistration): () => void {
@@ -284,27 +294,28 @@ function toggleSection(key: string): void {
284
294
  }
285
295
 
286
296
  function getSectionStyle(key: string): SidebarSectionStyle {
297
+ void geometryRevision.value;
287
298
  const state = currentState()[key];
288
299
  if (!state) throw new Error(`SSidebarGroup: cannot style unknown section '${key}'`);
289
300
  if (!state.isOpen) {
290
301
  return Object.freeze({ flexGrow: 0, flexShrink: 0, flexBasis: 'auto', overflow: 'hidden' });
291
302
  }
292
- if (state.size !== null) {
293
- return Object.freeze({
294
- flexBasis: `${state.size}px`,
295
- flexGrow: 0,
296
- flexShrink: 0,
297
- minHeight: `${state.minSize}px`,
298
- });
299
- }
303
+ const effectiveSize = effectiveSizes.get(key) ?? state.size ?? state.minSize;
300
304
  return Object.freeze({
301
- flexGrow: 1,
302
- flexShrink: 1,
303
- flexBasis: '0%',
304
- minHeight: `${state.minSize}px`,
305
+ flexGrow: 0,
306
+ flexShrink: 0,
307
+ flexBasis: `${effectiveSize}px`,
308
+ minHeight: 'var(--s-sidebar-collapsed-section-height)',
305
309
  });
306
310
  }
307
311
 
312
+ function getSectionSize(key: string): number {
313
+ void geometryRevision.value;
314
+ const section = registeredSection(key);
315
+ if (!section.state.isOpen) return measuredHeight(section.element, `sections.${key}`);
316
+ return effectiveSizes.get(key) ?? section.state.size ?? section.state.minSize;
317
+ }
318
+
308
319
  function resizePair(
309
320
  topKey: string,
310
321
  bottomKey: string,
@@ -336,7 +347,7 @@ function beginResize(topKey: string, bottomKey: string): void {
336
347
  );
337
348
  }
338
349
  activeResizePair = Object.freeze({ topKey, bottomKey });
339
- geometryRevision += 1;
350
+ geometryRevision.value += 1;
340
351
  }
341
352
 
342
353
  function resizeSections(topKey: string, bottomKey: string, requestedTopSize: number): void {
@@ -349,45 +360,51 @@ function resizeSections(topKey: string, bottomKey: string, requestedTopSize: num
349
360
  ) {
350
361
  throw new Error('SSidebarGroup: resize update does not match the active section pair');
351
362
  }
352
- const [top, bottom] = resizePair(topKey, bottomKey);
353
- const topHeight = Math.max(
354
- top.state.minSize,
355
- measuredHeight(top.element, `sections.${top.key}`),
356
- );
357
- const bottomHeight = Math.max(
358
- bottom.state.minSize,
359
- measuredHeight(bottom.element, `sections.${bottom.key}`),
363
+ resizePair(topKey, bottomKey);
364
+ const openSections = orderedSections.value.filter((section) => section.state.isOpen);
365
+ const topOpenIndex = openSections.findIndex((section) => section.key === topKey);
366
+ const upper = openSections.slice(0, topOpenIndex + 1);
367
+ const lower = openSections.slice(topOpenIndex + 1);
368
+ const currentUpperSize = upper.reduce(
369
+ (sum, section) => sum + (effectiveSizes.get(section.key) ?? measuredHeight(section.element, `sections.${section.key}`)),
370
+ 0,
360
371
  );
361
- const minimumPairSize = top.state.minSize + bottom.state.minSize;
362
- const measuredPairSize = Math.max(minimumPairSize, topHeight + bottomHeight);
363
- const containerHeight = measuredContainerHeight();
364
- const occupiedByOthers = orderedSections.value.reduce((sum, section) => {
365
- if (section.key === topKey || section.key === bottomKey) return sum;
366
- return sum + measuredHeight(section.element, `sections.${section.key}`);
367
- }, 0);
368
- const availablePairSize = containerHeight > 0
369
- ? Math.max(minimumPairSize, containerHeight - occupiedByOthers)
370
- : measuredPairSize;
371
- const pairSize = Math.max(minimumPairSize, Math.min(measuredPairSize, availablePairSize));
372
- const maximumTopSize = pairSize - bottom.state.minSize;
373
- const nextTopSize = Math.min(maximumTopSize, Math.max(top.state.minSize, requestedTopSize));
374
- const nextBottomSize = pairSize - nextTopSize;
375
- const flexibleOwner = top.state.size === null ? bottom.key : (
376
- bottom.state.size === null ? bottom.key : null
372
+ const delta = requestedTopSize - currentUpperSize;
373
+ const shrinking = delta > 0 ? [...lower] : [...upper].reverse();
374
+ const growing = delta > 0 ? upper.at(-1) : lower[0];
375
+ let transferable = Math.abs(delta);
376
+ transferable = Math.min(
377
+ transferable,
378
+ shrinking.reduce((sum, section) => {
379
+ const size = effectiveSizes.get(section.key) ?? measuredHeight(section.element, `sections.${section.key}`);
380
+ return sum + Math.max(0, size - section.state.minSize);
381
+ }, 0),
377
382
  );
378
- const nextTop: SectionState = { ...top.state, size: nextTopSize };
379
- const nextBottom: SectionState = {
380
- ...bottom.state,
381
- size: flexibleOwner === bottom.key ? null : nextBottomSize,
382
- };
383
- if (
384
- nextTop.size === top.state.size
385
- && nextBottom.size === bottom.state.size
386
- ) return;
387
- emitTransition([
388
- { key: top.key, state: nextTop },
389
- { key: bottom.key, state: nextBottom },
390
- ]);
383
+ if (transferable === 0 || !growing) return;
384
+
385
+ let remaining = transferable;
386
+ for (const section of shrinking) {
387
+ if (remaining <= 0) break;
388
+ const size = effectiveSizes.get(section.key) ?? measuredHeight(section.element, `sections.${section.key}`);
389
+ const reduction = Math.min(size - section.state.minSize, remaining);
390
+ effectiveSizes.set(section.key, size - reduction);
391
+ remaining -= reduction;
392
+ }
393
+ const growSize = effectiveSizes.get(growing.key)
394
+ ?? measuredHeight(growing.element, `sections.${growing.key}`);
395
+ effectiveSizes.set(growing.key, growSize + transferable);
396
+ geometryRevision.value += 1;
397
+
398
+ const replacements = openSections
399
+ .filter((section) => section.state.size !== null)
400
+ .map((section) => ({
401
+ key: section.key,
402
+ state: {
403
+ ...section.state,
404
+ size: effectiveSizes.get(section.key)!,
405
+ } satisfies SectionState,
406
+ }));
407
+ if (replacements.length > 0) emitTransition(replacements);
391
408
  }
392
409
 
393
410
  function finishResize(topKey: string, bottomKey: string): void {
@@ -404,9 +421,11 @@ function finishResize(topKey: string, bottomKey: string): void {
404
421
  provide(sidebarGroupContextKey, {
405
422
  sectionsState: computed(currentState),
406
423
  orderedSections,
424
+ geometryRevision,
407
425
  registerSection,
408
426
  toggleSection,
409
427
  getSectionStyle,
428
+ getSectionSize,
410
429
  beginResize,
411
430
  resizeSections,
412
431
  finishResize,
@@ -432,7 +451,7 @@ onMounted(() => {
432
451
 
433
452
  onBeforeUnmount(() => {
434
453
  mounted = false;
435
- geometryRevision += 1;
454
+ geometryRevision.value += 1;
436
455
  activeResizePair = null;
437
456
  resizeObserver?.disconnect();
438
457
  resizeObserver = null;
@@ -3,11 +3,13 @@
3
3
  .s-sidebar-section-wrapper {
4
4
  @apply flex flex-col;
5
5
  min-height: 0;
6
+ overflow: hidden;
6
7
  }
7
8
 
8
9
  .s-sidebar-section {
9
- @apply flex flex-col;
10
- @apply min-h-0 flex-grow w-full;
10
+ @apply flex flex-col min-h-0 w-full;
11
+ flex: 1 1 0%;
12
+ overflow: hidden;
11
13
  }
12
14
 
13
15
  .s-sidebar-section-wrapper + .s-sidebar-section-wrapper .s-sidebar-section {
@@ -19,9 +21,18 @@
19
21
  }
20
22
 
21
23
  .s-sidebar-section-header {
22
- @apply flex w-full items-center gap-1 px-2 h-7;
24
+ @apply flex w-full items-center gap-1 px-1 h-7;
25
+ @apply bg-surface-50/70 dark:bg-surface-800/55;
23
26
  @apply text-surface-600 dark:text-surface-300;
24
27
  @apply shrink-0;
28
+ transition:
29
+ background-color var(--s-motion-standard) ease,
30
+ color var(--s-motion-standard) ease;
31
+ }
32
+
33
+ .s-sidebar-section-header:has([data-sidebar-section-toggle]:hover),
34
+ .s-sidebar-section-header:has([data-sidebar-section-toggle]:focus-visible) {
35
+ @apply bg-surface-100 text-surface-800 dark:bg-surface-700/70 dark:text-surface-100;
25
36
  }
26
37
  .s-sidebar-section-header--mobile {
27
38
  @apply h-6 px-1.5;
@@ -37,5 +48,6 @@
37
48
 
38
49
  .s-sidebar-section-content {
39
50
  @apply text-sm text-surface-700 dark:text-surface-300;
40
- @apply flex-grow overflow-y-auto min-h-0;
51
+ @apply overflow-y-auto overflow-x-hidden min-h-0;
52
+ flex: 1 1 0%;
41
53
  }
@@ -26,6 +26,8 @@
26
26
  <SButton
27
27
  :id="triggerId"
28
28
  :size="layout === 'mobile' ? 'xs' : 'sm'"
29
+ spacing="tight"
30
+ hover-surface="parent"
29
31
  severity="secondary"
30
32
  appearance="ghost"
31
33
  shape="row"
@@ -208,14 +210,8 @@ const topSectionIndex = computed(() => {
208
210
  return -1;
209
211
  });
210
212
 
211
- function measuredHeight(section: RegisteredSidebarSection): number {
212
- const height = section.element.offsetHeight;
213
- if (!Number.isFinite(height) || height < 0) {
214
- throw new RangeError(
215
- `SSidebarSection: sections.${section.key}.offsetHeight must be a non-negative finite number`,
216
- );
217
- }
218
- return height;
213
+ function effectiveHeight(section: RegisteredSidebarSection): number {
214
+ return Math.max(section.state.minSize, groupContext.getSectionSize(section.key));
219
215
  }
220
216
 
221
217
  function resizePair(): readonly [RegisteredSidebarSection, RegisteredSidebarSection] {
@@ -234,17 +230,20 @@ function resizePair(): readonly [RegisteredSidebarSection, RegisteredSidebarSect
234
230
  }
235
231
 
236
232
  const resizeContract = computed(() => {
233
+ void groupContext.geometryRevision.value;
237
234
  const [top, bottom] = resizePair();
238
- const value = Math.max(top.state.minSize, top.state.size ?? measuredHeight(top));
239
- const bottomSize = Math.max(
240
- bottom.state.minSize,
241
- bottom.state.size ?? measuredHeight(bottom),
242
- );
243
- const pairSize = value + bottomSize;
235
+ const openSections = groupContext.orderedSections.value.filter((section) => section.state.isOpen);
236
+ const topIndex = openSections.findIndex((section) => section.key === top.key);
237
+ const upper = openSections.slice(0, topIndex + 1);
238
+ const lower = openSections.slice(topIndex + 1);
239
+ const value = upper.reduce((sum, section) => sum + effectiveHeight(section), 0);
240
+ const minimumUpper = upper.reduce((sum, section) => sum + section.state.minSize, 0);
241
+ const minimumLower = lower.reduce((sum, section) => sum + section.state.minSize, 0);
242
+ const totalSize = value + lower.reduce((sum, section) => sum + effectiveHeight(section), 0);
244
243
  return {
245
244
  value,
246
- min: top.state.minSize,
247
- max: Math.max(top.state.minSize, pairSize - bottom.state.minSize),
245
+ min: minimumUpper,
246
+ max: Math.max(minimumUpper, totalSize - minimumLower),
248
247
  testId: `sidebar-resize-${top.key}-${bottom.key}`,
249
248
  };
250
249
  });
@@ -33,12 +33,29 @@
33
33
  font-size: 0.75rem;
34
34
  }
35
35
 
36
+ .s-button[data-density='compact'][data-size='xs']:not([data-icon-only='true']) {
37
+ height: var(--s-control-hitbox-xs);
38
+ min-height: var(--s-control-hitbox-xs);
39
+ padding-block: 0;
40
+ }
41
+
36
42
  .s-button[data-size='sm']:not([data-icon-only='true']) {
37
43
  min-height: var(--s-control-hitbox-sm);
38
44
  padding: 0.375rem 0.625rem;
39
45
  font-size: 0.75rem;
40
46
  }
41
47
 
48
+ .s-button[data-spacing='tight'][data-size='sm']:not([data-icon-only='true']) {
49
+ height: var(--s-control-hitbox-sm);
50
+ min-height: var(--s-control-hitbox-sm);
51
+ padding-block: 0;
52
+ padding-inline: 0.25rem;
53
+ }
54
+
55
+ .s-button[data-spacing='tight'] .s-button__content {
56
+ gap: 0.25rem;
57
+ }
58
+
42
59
  .s-button[data-size='md']:not([data-icon-only='true']) {
43
60
  min-height: var(--s-control-hitbox-md);
44
61
  padding: 0.625rem 1.25rem;
@@ -129,6 +146,10 @@
129
146
  background: var(--s-button-solid-background-hover);
130
147
  }
131
148
 
149
+ .s-button[aria-disabled='true'] {
150
+ pointer-events: none;
151
+ }
152
+
132
153
  .s-button[data-appearance='solid']:active:not(:disabled) {
133
154
  border-color: var(--s-button-solid-background-active);
134
155
  background: var(--s-button-solid-background-active);
@@ -154,6 +175,10 @@
154
175
  background: var(--s-button-hover-background);
155
176
  }
156
177
 
178
+ .s-button[data-hover-surface='parent'][data-appearance='ghost']:hover:not(:disabled) {
179
+ background: transparent;
180
+ }
181
+
157
182
  .s-button[data-appearance='ghost'],
158
183
  .s-button[data-appearance='text'] {
159
184
  background: transparent;
@@ -198,6 +223,14 @@
198
223
  cursor: not-allowed;
199
224
  }
200
225
 
226
+ .s-button[aria-disabled='true'] {
227
+ border-color: transparent;
228
+ background: var(--s-button-disabled-background);
229
+ box-shadow: none;
230
+ color: var(--s-button-disabled-foreground);
231
+ cursor: not-allowed;
232
+ }
233
+
201
234
  .s-button[data-appearance='ghost']:disabled,
202
235
  .s-button[data-appearance='text']:disabled {
203
236
  background: transparent;
@@ -225,6 +258,7 @@
225
258
  .s-button[data-width='fit'] { width: fit-content; }
226
259
  .s-button[data-width='full'] .s-button__content { width: 100%; }
227
260
  .s-button[data-content-align='start'] .s-button__content { justify-content: flex-start; text-align: left; }
261
+ .s-button[data-content-align='end'] .s-button__content { justify-content: flex-end; text-align: right; }
228
262
  .s-button[data-content-align='between'] .s-button__content { justify-content: space-between; text-align: left; }
229
263
 
230
264
  .s-button[data-shape='pill'],
@@ -262,6 +296,11 @@
262
296
  height: var(--s-icon-size-sm);
263
297
  }
264
298
 
299
+ .s-button[data-icon-size='large'] .s-button__icon {
300
+ width: var(--s-icon-size-md);
301
+ height: var(--s-icon-size-md);
302
+ }
303
+
265
304
  .s-button__loader {
266
305
  @apply absolute inline-flex;
267
306
  }