@skewedaspect/sleekspace-ui 0.8.1 → 0.9.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 (177) hide show
  1. package/dist/components/Dropdown/SkDropdown.vue.d.ts +9 -1
  2. package/dist/components/Dropdown/types.d.ts +2 -1
  3. package/dist/components/NavBar/SkNavBar.vue.d.ts +9 -1
  4. package/dist/components/NavBar/context.d.ts +2 -0
  5. package/dist/components/NavBar/types.d.ts +5 -1
  6. package/dist/components/Page/SkPage.vue.d.ts +9 -0
  7. package/dist/components/ScrollArea/SkScrollArea.vue.d.ts +105 -4
  8. package/dist/composables/useCustomColors.d.ts +18 -56
  9. package/{src → dist}/global.d.ts +6 -2
  10. package/dist/sleekspace-ui.css +4253 -1251
  11. package/dist/sleekspace-ui.es.js +204 -109
  12. package/dist/sleekspace-ui.umd.js +204 -109
  13. package/dist/static/classes.d.ts +18 -0
  14. package/dist/static/components/alert.d.ts +12 -0
  15. package/dist/static/components/avatar.d.ts +9 -0
  16. package/dist/static/components/breadcrumbs.d.ts +6 -0
  17. package/dist/static/components/button.d.ts +13 -0
  18. package/dist/static/components/card.d.ts +5 -0
  19. package/dist/static/components/checkbox.d.ts +10 -0
  20. package/dist/static/components/colorPicker.d.ts +8 -0
  21. package/dist/static/components/divider.d.ts +8 -0
  22. package/dist/static/components/dropdown.d.ts +8 -0
  23. package/dist/static/components/field.d.ts +15 -0
  24. package/dist/static/components/group.d.ts +5 -0
  25. package/dist/static/components/input.d.ts +14 -0
  26. package/dist/static/components/navBar.d.ts +16 -0
  27. package/dist/static/components/numberInput.d.ts +15 -0
  28. package/dist/static/components/page.d.ts +9 -0
  29. package/dist/static/components/pagination.d.ts +5 -0
  30. package/dist/static/components/panel.d.ts +11 -0
  31. package/dist/static/components/progress.d.ts +9 -0
  32. package/dist/static/components/radio.d.ts +11 -0
  33. package/dist/static/components/select.d.ts +10 -0
  34. package/dist/static/components/sidebar.d.ts +9 -0
  35. package/dist/static/components/skeleton.d.ts +11 -0
  36. package/dist/static/components/slider.d.ts +12 -0
  37. package/dist/static/components/spinner.d.ts +12 -0
  38. package/dist/static/components/switchInput.d.ts +10 -0
  39. package/dist/static/components/table.d.ts +12 -0
  40. package/dist/static/components/tag.d.ts +8 -0
  41. package/dist/static/components/tagsInput.d.ts +7 -0
  42. package/dist/static/components/textarea.d.ts +12 -0
  43. package/dist/static/components/toolbar.d.ts +12 -0
  44. package/dist/static/components/tooltip.d.ts +7 -0
  45. package/dist/static/escape.d.ts +2 -0
  46. package/dist/static/index.cjs.js +1 -0
  47. package/dist/static/index.d.ts +68 -0
  48. package/dist/static/index.es.js +732 -0
  49. package/dist/static/render.d.ts +12 -0
  50. package/dist/static/specs.d.ts +2 -0
  51. package/dist/static/types.d.ts +43 -0
  52. package/dist/tokens.css +322 -0
  53. package/dist/types/index.d.ts +36 -0
  54. package/docs/guides/installation.md +8 -2
  55. package/docs/guides/pure-css/_meta.yaml +8 -0
  56. package/docs/guides/pure-css/class-api.md +1070 -0
  57. package/docs/guides/pure-css/custom-elements.md +574 -0
  58. package/docs/guides/pure-css/index.md +86 -0
  59. package/docs/guides/pure-css/limitations.md +152 -0
  60. package/docs/guides/pure-css/static-helpers.md +1203 -0
  61. package/llms-full.txt +3736 -261
  62. package/package.json +16 -5
  63. package/src/components/Card/SkCard.vue +1 -0
  64. package/src/components/ContextMenu/SkContextMenuRadioGroup.vue +4 -1
  65. package/src/components/Dropdown/SkDropdown.vue +20 -3
  66. package/src/components/Dropdown/SkDropdownRadioGroup.vue +4 -1
  67. package/src/components/Dropdown/types.ts +2 -1
  68. package/src/components/NavBar/SkNavBar.vue +14 -4
  69. package/src/components/NavBar/context.ts +4 -2
  70. package/src/components/NavBar/types.ts +6 -1
  71. package/src/components/Page/SkPage.vue +11 -0
  72. package/src/components/Panel/SkPanel.vue +2 -1
  73. package/src/components/ScrollArea/SkScrollArea.vue +78 -5
  74. package/src/components/TreeView/SkTreeView.vue +7 -2
  75. package/src/composables/useCustomColors.ts +86 -77
  76. package/src/composables/usePortalContext.test.ts +0 -2
  77. package/src/shims.d.ts +10 -0
  78. package/src/static/__tests__/parity.test.ts +717 -0
  79. package/src/static/__tests__/parityHarness.test.ts +98 -0
  80. package/src/static/__tests__/parityHarness.ts +260 -0
  81. package/src/static/classes.test.ts +82 -0
  82. package/src/static/classes.ts +111 -0
  83. package/src/static/components/__tests__/helpers.test.ts +837 -0
  84. package/src/static/components/alert.ts +117 -0
  85. package/src/static/components/avatar.ts +86 -0
  86. package/src/static/components/breadcrumbs.ts +28 -0
  87. package/src/static/components/button.ts +75 -0
  88. package/src/static/components/card.ts +27 -0
  89. package/src/static/components/checkbox.ts +48 -0
  90. package/src/static/components/colorPicker.ts +45 -0
  91. package/src/static/components/divider.ts +39 -0
  92. package/src/static/components/dropdown.ts +36 -0
  93. package/src/static/components/field.ts +86 -0
  94. package/src/static/components/group.ts +27 -0
  95. package/src/static/components/input.ts +55 -0
  96. package/src/static/components/navBar.ts +94 -0
  97. package/src/static/components/numberInput.ts +64 -0
  98. package/src/static/components/page.ts +31 -0
  99. package/src/static/components/pagination.ts +27 -0
  100. package/src/static/components/panel.ts +33 -0
  101. package/src/static/components/progress.ts +31 -0
  102. package/src/static/components/radio.ts +53 -0
  103. package/src/static/components/select.ts +51 -0
  104. package/src/static/components/sidebar.ts +85 -0
  105. package/src/static/components/skeleton.ts +66 -0
  106. package/src/static/components/slider.ts +50 -0
  107. package/src/static/components/spinner.ts +94 -0
  108. package/src/static/components/switchInput.ts +49 -0
  109. package/src/static/components/table.ts +88 -0
  110. package/src/static/components/tag.ts +76 -0
  111. package/src/static/components/tagsInput.ts +35 -0
  112. package/src/static/components/textarea.ts +53 -0
  113. package/src/static/components/toolbar.ts +74 -0
  114. package/src/static/components/tooltip.ts +29 -0
  115. package/src/static/escape.test.ts +53 -0
  116. package/src/static/escape.ts +28 -0
  117. package/src/static/generated/defaults.ts +378 -0
  118. package/src/static/generated/propTypes.ts +425 -0
  119. package/src/static/index.ts +116 -0
  120. package/src/static/render.test.ts +83 -0
  121. package/src/static/render.ts +76 -0
  122. package/src/static/specs.test.ts +58 -0
  123. package/src/static/specs.ts +230 -0
  124. package/src/static/types.ts +176 -0
  125. package/src/styles/__tests__/testHelpers.ts +97 -0
  126. package/src/styles/base/_custom-elements.scss +51 -0
  127. package/src/styles/base/_index.scss +4 -0
  128. package/src/styles/components/__tests__/componentSelectors.test.ts +2575 -0
  129. package/src/styles/components/_alert.scss +82 -39
  130. package/src/styles/components/_avatar.scss +102 -47
  131. package/src/styles/components/_breadcrumbs.scss +39 -37
  132. package/src/styles/components/_button.scss +58 -5
  133. package/src/styles/components/_card.scss +64 -2
  134. package/src/styles/components/_checkbox.scss +35 -5
  135. package/src/styles/components/_color-picker.scss +48 -13
  136. package/src/styles/components/_divider.scss +86 -52
  137. package/src/styles/components/_dropdown.scss +214 -0
  138. package/src/styles/components/_field.scss +76 -23
  139. package/src/styles/components/_group.scss +190 -79
  140. package/src/styles/components/_index.scss +1 -0
  141. package/src/styles/components/_input.scss +81 -5
  142. package/src/styles/components/_menu.scss +1 -1
  143. package/src/styles/components/_navbar.scss +76 -45
  144. package/src/styles/components/_number-input.scss +88 -83
  145. package/src/styles/components/_page.scss +82 -23
  146. package/src/styles/components/_pagination.scss +240 -212
  147. package/src/styles/components/_panel.scss +268 -122
  148. package/src/styles/components/_progress.scss +120 -70
  149. package/src/styles/components/_radio.scss +35 -5
  150. package/src/styles/components/_scroll-area.scss +50 -22
  151. package/src/styles/components/_select.scss +40 -9
  152. package/src/styles/components/_sidebar.scss +59 -34
  153. package/src/styles/components/_skeleton.scss +111 -65
  154. package/src/styles/components/_slider.scss +34 -10
  155. package/src/styles/components/_spinner.scss +107 -56
  156. package/src/styles/components/_switch.scss +36 -5
  157. package/src/styles/components/_table.scss +150 -166
  158. package/src/styles/components/_tag.scss +244 -154
  159. package/src/styles/components/_tags-input.scss +46 -12
  160. package/src/styles/components/_textarea.scss +36 -5
  161. package/src/styles/components/_toolbar.scss +85 -31
  162. package/src/styles/components/_tooltip.scss +172 -3
  163. package/src/styles/mixins/_cut-border.scss +18 -4
  164. package/src/styles/mixins/_dual-selector.scss +192 -0
  165. package/src/styles/mixins/_index.scss +1 -0
  166. package/src/styles/mixins/dualSelector.test.ts +151 -0
  167. package/src/styles/themes/_colorful.scss +25 -0
  168. package/src/styles/themes/_greyscale.scss +25 -0
  169. package/src/styles/themes/_shade-scale.scss +39 -0
  170. package/src/styles/tokens/_semantic-color-kinds.scss +66 -0
  171. package/src/{types.ts → types/index.ts} +19 -11
  172. package/web-types.json +970 -137
  173. package/dist/composables/useCustomColors.test.d.ts +0 -1
  174. package/dist/composables/useFocusTrap.test.d.ts +0 -1
  175. package/dist/composables/usePortalContext.test.d.ts +0 -1
  176. package/dist/styles/mixins/fluidSize.test.d.ts +0 -1
  177. package/dist/types.d.ts +0 -29
@@ -0,0 +1,117 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Alert Static Helper
3
+ //
4
+ // Emits the full alert DOM structure: optional icon container + content wrapper, matching the
5
+ // Vue component's rendered output. Icon is auto-selected by kind for the four feedback kinds
6
+ // (info, success, warning, danger). Pass `icon: false` to suppress it, or `icon: '<html>'`
7
+ // for a custom icon.
8
+ //----------------------------------------------------------------------------------------------------------------------
9
+
10
+ import type { AlertKind, StaticCustomColors } from '../types';
11
+
12
+ import { composeClasses } from '../classes';
13
+ import { escapeAttr } from '../escape';
14
+
15
+ //----------------------------------------------------------------------------------------------------------------------
16
+
17
+ // Feedback kinds that have a default icon — mirrors FEEDBACK_KINDS in the Vue component.
18
+ const FEEDBACK_KINDS = new Set([ 'info', 'success', 'warning', 'danger' ]);
19
+
20
+ // Default SVG icons per feedback kind, matching the exact SVG content in SkAlert.vue.
21
+ const DEFAULT_ICONS : Record<string, string>
22
+ = {
23
+ info: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">'
24
+ + '<circle cx="12" cy="12" r="10"></circle>'
25
+ + '<line x1="12" y1="16" x2="12" y2="12"></line>'
26
+ + '<circle cx="12" cy="8" r="0.5" fill="currentColor"></circle>'
27
+ + '</svg>',
28
+
29
+ success: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">'
30
+ + '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>'
31
+ + '<polyline points="22 4 12 14.01 9 11.01"></polyline>'
32
+ + '</svg>',
33
+
34
+ warning: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">'
35
+ + '<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z">'
36
+ + '</path>'
37
+ + '<line x1="12" y1="9" x2="12" y2="13"></line>'
38
+ + '<circle cx="12" cy="17" r="0.5" fill="currentColor"></circle>'
39
+ + '</svg>',
40
+
41
+ danger: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">'
42
+ + '<circle cx="12" cy="12" r="10"></circle>'
43
+ + '<line x1="15" y1="9" x2="9" y2="15"></line>'
44
+ + '<line x1="9" y1="9" x2="15" y2="15"></line>'
45
+ + '</svg>',
46
+ };
47
+
48
+ //----------------------------------------------------------------------------------------------------------------------
49
+
50
+ export interface AlertStaticProps extends StaticCustomColors
51
+ {
52
+ kind ?: AlertKind;
53
+ subtle ?: boolean;
54
+
55
+ /**
56
+ * Custom icon HTML string, or `false` to suppress the icon entirely.
57
+ * When omitted (default), a kind-appropriate SVG is shown for feedback kinds
58
+ * (info, success, warning, danger); non-feedback kinds show no icon.
59
+ */
60
+ icon ?: string | false;
61
+ }
62
+
63
+ //----------------------------------------------------------------------------------------------------------------------
64
+
65
+ export function alert(props : AlertStaticProps = {}, children = '') : string
66
+ {
67
+ const kind = props.kind ?? 'info';
68
+
69
+ // Apply default kind so the class is always emitted (mirrors Vue's withDefaults({ kind: 'info' }))
70
+ const propsWithDefaults = { ...props, kind };
71
+
72
+ const classes = composeClasses(
73
+ {
74
+ base: 'sk-alert',
75
+ kind: true,
76
+ booleanFlags: [ 'subtle' ],
77
+ },
78
+ propsWithDefaults as Record<string, unknown>
79
+ );
80
+
81
+ const attrs : string[] = [ `class="${ escapeAttr(classes) }"`, 'role="alert"' ];
82
+
83
+ // Custom color vars — match the Vue component's useCustomColors('alert', ...) output.
84
+ const styleParts : string[] = [];
85
+ if(typeof props.baseColor === 'string')
86
+ {
87
+ styleParts.push(`--sk-alert-color-base: ${ escapeAttr(props.baseColor) };`);
88
+ }
89
+ if(typeof props.textColor === 'string')
90
+ {
91
+ styleParts.push(`--sk-alert-fg: ${ escapeAttr(props.textColor) };`);
92
+ }
93
+ if(styleParts.length > 0)
94
+ {
95
+ attrs.push(`style="${ styleParts.join(' ') }"`);
96
+ }
97
+
98
+ // Determine icon HTML: explicit false = no icon; explicit string = custom; undefined = auto
99
+ let iconHtml = '';
100
+ if(props.icon !== false)
101
+ {
102
+ const iconContent = typeof props.icon === 'string'
103
+ ? props.icon
104
+ : (FEEDBACK_KINDS.has(kind) ? (DEFAULT_ICONS[kind] ?? '') : null);
105
+
106
+ if(iconContent !== null)
107
+ {
108
+ iconHtml = `<div class="sk-alert-icon">${ iconContent }</div>`;
109
+ }
110
+ }
111
+
112
+ const contentHtml = `<div class="sk-alert-content">${ children }</div>`;
113
+
114
+ return `<div ${ attrs.join(' ') }>${ iconHtml }${ contentHtml }</div>`;
115
+ }
116
+
117
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,86 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Avatar Static Helper
3
+ //
4
+ // Emits the full avatar DOM structure matching the Vue component's rendered output:
5
+ // <div class="sk-avatar sk-<kind> sk-<size>" [style="..."]>
6
+ // <img class="sk-avatar-image" src="..." alt="..."> (when src provided)
7
+ // <span class="sk-avatar-initials">AB</span> (when initials provided, no src)
8
+ // <svg class="sk-avatar-icon" ...>…</svg> (default fallback — no src or initials)
9
+ // </div>
10
+ //
11
+ // Fallback priority matches Vue: src → initials → default SVG icon.
12
+ // No `shape` prop — Vue's SkAvatar has no shape prop; that was a prior helper addition that
13
+ // diverged from Vue's actual API. Removed here to match Vue's class output exactly.
14
+ //----------------------------------------------------------------------------------------------------------------------
15
+
16
+ import type { AvatarSize, ComponentKind, StaticCustomColors } from '../types';
17
+
18
+ import { escapeAttr } from '../escape';
19
+
20
+ //----------------------------------------------------------------------------------------------------------------------
21
+
22
+ export interface AvatarStaticProps extends StaticCustomColors
23
+ {
24
+ kind ?: ComponentKind;
25
+ size ?: AvatarSize;
26
+ src ?: string;
27
+ alt ?: string;
28
+ initials ?: string;
29
+ }
30
+
31
+ //----------------------------------------------------------------------------------------------------------------------
32
+
33
+ // Default fallback SVG — exact content from SkAvatar.vue's default #icon slot
34
+ const DEFAULT_ICON_PATH = 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4'
35
+ + 'v2h16v-2c0-2.66-5.33-4-8-4z';
36
+ const DEFAULT_ICON_SVG = '<svg class="sk-avatar-icon" viewBox="0 0 24 24" fill="currentColor">'
37
+ + `<path d="${ DEFAULT_ICON_PATH }"></path>`
38
+ + '</svg>';
39
+
40
+ //----------------------------------------------------------------------------------------------------------------------
41
+
42
+ export function avatar(props : AvatarStaticProps = {}) : string
43
+ {
44
+ const kind = props.kind ?? 'neutral';
45
+ const size = props.size ?? 'md';
46
+
47
+ const classes = [ 'sk-avatar', `sk-${ kind }`, `sk-${ size }` ];
48
+ const attrs : string[] = [ `class="${ escapeAttr(classes.join(' ')) }"` ];
49
+
50
+ // Custom color vars — match Vue's customStyles computed property
51
+ const styleParts : string[] = [];
52
+ if(typeof props.baseColor === 'string')
53
+ {
54
+ styleParts.push(`--sk-avatar-color-base: ${ escapeAttr(props.baseColor) };`);
55
+ }
56
+ if(typeof props.textColor === 'string')
57
+ {
58
+ styleParts.push(`--sk-avatar-fg: ${ escapeAttr(props.textColor) };`);
59
+ }
60
+ if(styleParts.length > 0)
61
+ {
62
+ attrs.push(`style="${ styleParts.join(' ') }"`);
63
+ }
64
+
65
+ // Inner content: image > initials > default icon (matches Vue's v-if / v-else-if / v-else)
66
+ let inner : string;
67
+ if(props.src)
68
+ {
69
+ const alt = props.alt ?? '';
70
+ inner = `<img src="${ escapeAttr(props.src) }" alt="${ escapeAttr(alt) }" class="sk-avatar-image">`;
71
+ }
72
+ else if(props.initials)
73
+ {
74
+ // Truncate to 2 chars, uppercase — matches Vue's displayInitials computed
75
+ const display = props.initials.slice(0, 2).toUpperCase();
76
+ inner = `<span class="sk-avatar-initials">${ escapeAttr(display) }</span>`;
77
+ }
78
+ else
79
+ {
80
+ inner = DEFAULT_ICON_SVG;
81
+ }
82
+
83
+ return `<div ${ attrs.join(' ') }>${ inner }</div>`;
84
+ }
85
+
86
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,28 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Breadcrumbs Static Helper
3
+ //
4
+ // Thin typed wrapper over the generic render() core. Emits a class-API <nav aria-label="Breadcrumbs">
5
+ // with sk-breadcrumbs modifier classes derived from props.
6
+ //----------------------------------------------------------------------------------------------------------------------
7
+
8
+ import type { BreadcrumbsKind, StaticCustomColors } from '../types';
9
+
10
+ import { render } from '../render';
11
+ import { SPECS } from '../specs';
12
+
13
+ //----------------------------------------------------------------------------------------------------------------------
14
+
15
+ export interface BreadcrumbsStaticProps extends StaticCustomColors
16
+ {
17
+ kind ?: BreadcrumbsKind;
18
+ separator ?: string;
19
+ }
20
+
21
+ //----------------------------------------------------------------------------------------------------------------------
22
+
23
+ export function breadcrumbs(props : BreadcrumbsStaticProps = {}, children = '') : string
24
+ {
25
+ return render(SPECS.breadcrumbs, props as Record<string, unknown>, children);
26
+ }
27
+
28
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,75 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Button Static Helper
3
+ //
4
+ // Renders <button> (default) or <a> (when href is provided) with the required chrome-wrapper
5
+ // span. Never renders as a custom element. For router-link use cases, the consumer renders
6
+ // their own <a> with class="sk-button" — this helper emits plain HTML only.
7
+ //----------------------------------------------------------------------------------------------------------------------
8
+
9
+ import type { ButtonKind, ButtonSize, ButtonType, ButtonVariant, StaticCustomColors } from '../types';
10
+
11
+ import { composeClasses } from '../classes';
12
+ import { escapeAttr } from '../escape';
13
+
14
+ //----------------------------------------------------------------------------------------------------------------------
15
+
16
+ export interface ButtonStaticProps extends StaticCustomColors
17
+ {
18
+ type ?: ButtonType;
19
+ kind ?: ButtonKind;
20
+ variant ?: ButtonVariant;
21
+ size ?: ButtonSize;
22
+ disabled ?: boolean;
23
+ loading ?: boolean;
24
+ pressed ?: boolean;
25
+ dense ?: boolean;
26
+ href ?: string;
27
+ }
28
+
29
+ //----------------------------------------------------------------------------------------------------------------------
30
+
31
+ export function button(props : ButtonStaticProps = {}, children = '') : string
32
+ {
33
+ const classes = composeClasses({
34
+ base: 'sk-button',
35
+ kind: true,
36
+ size: true,
37
+ variant: true,
38
+ booleanFlags: [ 'loading', 'pressed', 'dense' ],
39
+ }, props as Record<string, unknown>);
40
+
41
+ const attrs : string[] = [ `class="${ escapeAttr(classes) }"` ];
42
+ const isAnchor = typeof props.href === 'string';
43
+
44
+ if(isAnchor)
45
+ {
46
+ attrs.push(`href="${ escapeAttr(props.href ?? '') }"`);
47
+ }
48
+ else
49
+ {
50
+ const buttonType = props.type ?? 'button';
51
+ attrs.push(`type="${ escapeAttr(buttonType) }"`);
52
+ }
53
+
54
+ if(props.disabled)
55
+ {
56
+ attrs.push(isAnchor ? 'aria-disabled="true"' : 'disabled');
57
+ }
58
+
59
+ if(props.loading)
60
+ {
61
+ attrs.push('aria-busy="true"');
62
+ }
63
+
64
+ if(props.pressed)
65
+ {
66
+ attrs.push('aria-pressed="true"');
67
+ }
68
+
69
+ const tag = isAnchor ? 'a' : 'button';
70
+ const inner = `<span class="sk-button-chrome">${ children }</span>`;
71
+
72
+ return `<${ tag } ${ attrs.join(' ') }>${ inner }</${ tag }>`;
73
+ }
74
+
75
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,27 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Card Static Helper
3
+ //
4
+ // Thin typed wrapper over the generic render() core. Emits a class-API <div> with sk-card
5
+ // modifier classes derived from props.
6
+ //----------------------------------------------------------------------------------------------------------------------
7
+
8
+ import type { CardKind, StaticCustomColors } from '../types';
9
+
10
+ import { render } from '../render';
11
+ import { SPECS } from '../specs';
12
+
13
+ //----------------------------------------------------------------------------------------------------------------------
14
+
15
+ export interface CardStaticProps extends StaticCustomColors
16
+ {
17
+ kind ?: CardKind;
18
+ }
19
+
20
+ //----------------------------------------------------------------------------------------------------------------------
21
+
22
+ export function card(props : CardStaticProps = {}, children = '') : string
23
+ {
24
+ return render(SPECS.card, props as Record<string, unknown>, children);
25
+ }
26
+
27
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,48 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Checkbox Static Helper
3
+ //
4
+ // Emits a compound <label class="sk-checkbox"><input type="checkbox" /><span class="sk-checkbox-box"></span>
5
+ // <span class="sk-checkbox-label">children</span></label> structure for pure-CSS usage. This is
6
+ // the static/SSG-friendly alternative to the Reka UI–based Vue component.
7
+ //----------------------------------------------------------------------------------------------------------------------
8
+
9
+ import type { ComponentKind, ComponentSize } from '../types';
10
+
11
+ import { composeClasses } from '../classes';
12
+ import { escapeAttr } from '../escape';
13
+
14
+ //----------------------------------------------------------------------------------------------------------------------
15
+
16
+ export interface CheckboxStaticProps
17
+ {
18
+ kind ?: ComponentKind;
19
+ size ?: ComponentSize;
20
+ checked ?: boolean;
21
+ disabled ?: boolean;
22
+ required ?: boolean;
23
+ name ?: string;
24
+ }
25
+
26
+ //----------------------------------------------------------------------------------------------------------------------
27
+
28
+ export function checkbox(props : CheckboxStaticProps = {}, children = '') : string
29
+ {
30
+ const classes = composeClasses({ base: 'sk-checkbox', kind: true, size: true }, props as Record<string, unknown>);
31
+
32
+ const inputAttrs : string[] = [ 'type="checkbox"' ];
33
+ if(props.name)
34
+ {
35
+ inputAttrs.push(`name="${ escapeAttr(props.name) }"`);
36
+ }
37
+ if(props.checked === true) { inputAttrs.push('checked'); }
38
+ if(props.disabled === true) { inputAttrs.push('disabled'); }
39
+ if(props.required === true) { inputAttrs.push('required'); }
40
+
41
+ const inputEl = `<input ${ inputAttrs.join(' ') } />`;
42
+ const boxEl = `<span class="sk-checkbox-box"></span>`;
43
+ const labelEl = `<span class="sk-checkbox-label">${ children }</span>`;
44
+
45
+ return `<label class="${ escapeAttr(classes) }">${ inputEl }${ boxEl }${ labelEl }</label>`;
46
+ }
47
+
48
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,45 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // ColorPicker Static Helper
3
+ //
4
+ // Emits a void <input class="sk-color-picker" type="color" /> with passthrough attrs for
5
+ // value and name.
6
+ //----------------------------------------------------------------------------------------------------------------------
7
+
8
+ import type { ComponentSize } from '../types';
9
+
10
+ import { composeClasses } from '../classes';
11
+ import { escapeAttr } from '../escape';
12
+
13
+ //----------------------------------------------------------------------------------------------------------------------
14
+
15
+ export interface ColorPickerStaticProps
16
+ {
17
+ size ?: ComponentSize;
18
+ value ?: string;
19
+ name ?: string;
20
+ disabled ?: boolean;
21
+ }
22
+
23
+ //----------------------------------------------------------------------------------------------------------------------
24
+
25
+ export function colorPicker(props : ColorPickerStaticProps = {}) : string
26
+ {
27
+ const classes = composeClasses({ base: 'sk-color-picker', size: true }, props as Record<string, unknown>);
28
+ const attrs : string[] = [ `class="${ escapeAttr(classes) }"`, 'type="color"' ];
29
+
30
+ const passthroughString = [ 'value', 'name' ] as const;
31
+ for(const key of passthroughString)
32
+ {
33
+ const val = props[key];
34
+ if(typeof val === 'string')
35
+ {
36
+ attrs.push(`${ key }="${ escapeAttr(val) }"`);
37
+ }
38
+ }
39
+
40
+ if(props.disabled === true) { attrs.push('disabled'); }
41
+
42
+ return `<input ${ attrs.join(' ') } />`;
43
+ }
44
+
45
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,39 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Divider Static Helper
3
+ //
4
+ // Emits an <hr> element matching the Vue component's rendered output. The orientation class
5
+ // follows Vue's pattern: sk-horizontal / sk-vertical (not sk-orientation-*). Kind and size
6
+ // modifier classes are emitted exactly as Vue does, using the sk-${kind} and sk-${size}
7
+ // families. The aria-orientation attribute is omitted — it's filtered by the parity harness
8
+ // as a reka-ui runtime attribute and has no semantic value in a static context.
9
+ //----------------------------------------------------------------------------------------------------------------------
10
+
11
+ import type { ComponentKind, ComponentSize, DividerOrientation, DividerVariant } from '../types';
12
+
13
+ import { escapeAttr } from '../escape';
14
+
15
+ //----------------------------------------------------------------------------------------------------------------------
16
+
17
+ export interface DividerStaticProps
18
+ {
19
+ orientation ?: DividerOrientation;
20
+ kind ?: ComponentKind;
21
+ variant ?: DividerVariant;
22
+ size ?: ComponentSize;
23
+ }
24
+
25
+ //----------------------------------------------------------------------------------------------------------------------
26
+
27
+ export function divider(props : DividerStaticProps = {}) : string
28
+ {
29
+ const orientation = props.orientation ?? 'horizontal';
30
+ const kind = props.kind ?? 'neutral';
31
+ const size = props.size ?? 'md';
32
+
33
+ const classes : string[] = [ 'sk-divider', `sk-${ orientation }`, `sk-${ kind }`, `sk-${ size }` ];
34
+ if(props.variant === 'subtle') { classes.push('sk-subtle'); }
35
+
36
+ return `<hr class="${ escapeAttr(classes.join(' ')) }" role="separator">`;
37
+ }
38
+
39
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,36 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Dropdown Static Helper (<details> disclosure)
3
+ //
4
+ // Note: this is the pure-CSS disclosure variant, NOT a menu. The Vue SkDropdown is a menu with
5
+ // focus trap and keyboard navigation; this helper outputs a plain <details>/<summary>
6
+ // disclosure styled to match Sleekspace.
7
+ //----------------------------------------------------------------------------------------------------------------------
8
+
9
+ import type { SemanticKind } from '../types';
10
+
11
+ import { composeClasses } from '../classes';
12
+ import { escapeAttr } from '../escape';
13
+
14
+ //----------------------------------------------------------------------------------------------------------------------
15
+
16
+ export interface DropdownStaticProps
17
+ {
18
+ summary : string;
19
+ kind ?: SemanticKind;
20
+ size ?: 'sm' | 'md' | 'lg';
21
+ open ?: boolean;
22
+ }
23
+
24
+ //----------------------------------------------------------------------------------------------------------------------
25
+
26
+ export function dropdown(props : DropdownStaticProps, children = '') : string
27
+ {
28
+ const spec = { base: 'sk-dropdown', kind: true, size: true } as const;
29
+ const classes = composeClasses(spec, props as unknown as Record<string, unknown>);
30
+ const attrs : string[] = [ `class="${ escapeAttr(classes) }"` ];
31
+ if(props.open) { attrs.push('open'); }
32
+
33
+ return `<details ${ attrs.join(' ') }><summary>${ escapeAttr(props.summary) }</summary>${ children }</details>`;
34
+ }
35
+
36
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,86 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Field Static Helper
3
+ //
4
+ // Emits the full field DOM structure matching the Vue component's rendered output:
5
+ // <div class="sk-field sk-label-<position> [sk-has-error]">
6
+ // [<label for="<id>" class="sk-field-label">
7
+ // LABEL[<span class="sk-field-required">*</span>]
8
+ // </label>]
9
+ // <div class="sk-field-input-wrapper">CHILDREN</div>
10
+ // [<p id="<id>-description" class="sk-field-description">DESCRIPTION</p>]
11
+ // [<p id="<id>-error" class="sk-field-error">ERROR</p>]
12
+ // </div>
13
+ //
14
+ // Label position defaults to 'top' (sk-label-top) — matches Vue's withDefaults({ labelPosition: 'top' }).
15
+ // The sk-field-input-wrapper div is always emitted, wrapping the children slot.
16
+ //
17
+ // The `id` prop mirrors Vue's `id` prop — when provided it is used for the label `for` attribute
18
+ // and the description/error paragraph IDs, enabling DOM-accurate parity tests. When omitted, the
19
+ // label `for` and paragraph IDs are not emitted (Vue auto-generates them at runtime).
20
+ //----------------------------------------------------------------------------------------------------------------------
21
+
22
+ import type { FieldLabelPosition } from '../types';
23
+
24
+ import { escapeAttr } from '../escape';
25
+
26
+ //----------------------------------------------------------------------------------------------------------------------
27
+
28
+ export interface FieldStaticProps
29
+ {
30
+ label ?: string;
31
+ description ?: string;
32
+ error ?: string;
33
+ required ?: boolean;
34
+ labelPosition ?: FieldLabelPosition;
35
+
36
+ /**
37
+ * Explicit field ID — forwarded to label `for` and description/error `id` attributes.
38
+ * Mirrors Vue's `id` prop. When omitted, Vue auto-generates the ID and the static helper
39
+ * skips those attributes (avoid parity tests without an explicit `id`).
40
+ */
41
+ id ?: string;
42
+ }
43
+
44
+ //----------------------------------------------------------------------------------------------------------------------
45
+
46
+ export function field(props : FieldStaticProps = {}, children = '') : string
47
+ {
48
+ const labelPosition = props.labelPosition ?? 'top';
49
+
50
+ const classes : string[] = [ 'sk-field', `sk-label-${ labelPosition }` ];
51
+ if(props.error) { classes.push('sk-has-error'); }
52
+
53
+ const attrs = `class="${ escapeAttr(classes.join(' ')) }"`;
54
+
55
+ // Label element (only when label prop is set)
56
+ let labelHtml = '';
57
+ if(props.label)
58
+ {
59
+ const forAttr = props.id ? ` for="${ escapeAttr(props.id) }"` : '';
60
+ const requiredSpan = props.required ? '<span class="sk-field-required">*</span>' : '';
61
+ labelHtml = `<label${ forAttr } class="sk-field-label">${ escapeAttr(props.label) }${ requiredSpan }</label>`;
62
+ }
63
+
64
+ // Input wrapper always emitted
65
+ const wrapperHtml = `<div class="sk-field-input-wrapper">${ children }</div>`;
66
+
67
+ // Description (only when set and no error)
68
+ let descriptionHtml = '';
69
+ if(props.description && !props.error)
70
+ {
71
+ const idAttr = props.id ? ` id="${ escapeAttr(props.id) }-description"` : '';
72
+ descriptionHtml = `<p${ idAttr } class="sk-field-description">${ escapeAttr(props.description) }</p>`;
73
+ }
74
+
75
+ // Error (only when set)
76
+ let errorHtml = '';
77
+ if(props.error)
78
+ {
79
+ const idAttr = props.id ? ` id="${ escapeAttr(props.id) }-error"` : '';
80
+ errorHtml = `<p${ idAttr } class="sk-field-error">${ escapeAttr(props.error) }</p>`;
81
+ }
82
+
83
+ return `<div ${ attrs }>${ labelHtml }${ wrapperHtml }${ descriptionHtml }${ errorHtml }</div>`;
84
+ }
85
+
86
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,27 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Group Static Helper
3
+ //
4
+ // Thin typed wrapper over the generic render() core. Emits a class-API <div> with sk-group
5
+ // modifier classes derived from props.
6
+ //----------------------------------------------------------------------------------------------------------------------
7
+
8
+ import type { GroupOrientation } from '../types';
9
+
10
+ import { render } from '../render';
11
+ import { SPECS } from '../specs';
12
+
13
+ //----------------------------------------------------------------------------------------------------------------------
14
+
15
+ export interface GroupStaticProps
16
+ {
17
+ orientation ?: GroupOrientation;
18
+ }
19
+
20
+ //----------------------------------------------------------------------------------------------------------------------
21
+
22
+ export function group(props : GroupStaticProps = {}, children = '') : string
23
+ {
24
+ return render(SPECS.group, props as Record<string, unknown>, children);
25
+ }
26
+
27
+ //----------------------------------------------------------------------------------------------------------------------