@skewedaspect/sleekspace-ui 0.8.1 → 0.9.1

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 (191) 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/NumberInput/SkNumberInput.vue.d.ts +8 -0
  7. package/dist/components/Page/SkPage.vue.d.ts +9 -0
  8. package/dist/components/ScrollArea/SkScrollArea.vue.d.ts +105 -4
  9. package/dist/composables/useCustomColors.d.ts +18 -56
  10. package/{src → dist}/global.d.ts +6 -2
  11. package/dist/sleekspace-ui.css +4257 -1253
  12. package/dist/sleekspace-ui.es.js +300 -170
  13. package/dist/sleekspace-ui.umd.js +299 -169
  14. package/dist/static/classes.d.ts +18 -0
  15. package/dist/static/components/alert.d.ts +12 -0
  16. package/dist/static/components/avatar.d.ts +9 -0
  17. package/dist/static/components/breadcrumbs.d.ts +6 -0
  18. package/dist/static/components/button.d.ts +13 -0
  19. package/dist/static/components/card.d.ts +5 -0
  20. package/dist/static/components/checkbox.d.ts +10 -0
  21. package/dist/static/components/colorPicker.d.ts +8 -0
  22. package/dist/static/components/divider.d.ts +8 -0
  23. package/dist/static/components/dropdown.d.ts +8 -0
  24. package/dist/static/components/field.d.ts +15 -0
  25. package/dist/static/components/group.d.ts +5 -0
  26. package/dist/static/components/input.d.ts +14 -0
  27. package/dist/static/components/navBar.d.ts +16 -0
  28. package/dist/static/components/numberInput.d.ts +15 -0
  29. package/dist/static/components/page.d.ts +9 -0
  30. package/dist/static/components/pagination.d.ts +5 -0
  31. package/dist/static/components/panel.d.ts +11 -0
  32. package/dist/static/components/progress.d.ts +9 -0
  33. package/dist/static/components/radio.d.ts +11 -0
  34. package/dist/static/components/select.d.ts +10 -0
  35. package/dist/static/components/sidebar.d.ts +9 -0
  36. package/dist/static/components/skeleton.d.ts +11 -0
  37. package/dist/static/components/slider.d.ts +12 -0
  38. package/dist/static/components/spinner.d.ts +12 -0
  39. package/dist/static/components/switchInput.d.ts +10 -0
  40. package/dist/static/components/table.d.ts +12 -0
  41. package/dist/static/components/tag.d.ts +8 -0
  42. package/dist/static/components/tagsInput.d.ts +7 -0
  43. package/dist/static/components/textarea.d.ts +12 -0
  44. package/dist/static/components/toolbar.d.ts +12 -0
  45. package/dist/static/components/tooltip.d.ts +7 -0
  46. package/dist/static/escape.d.ts +2 -0
  47. package/dist/static/index.cjs.js +1 -0
  48. package/dist/static/index.d.ts +68 -0
  49. package/dist/static/index.es.js +732 -0
  50. package/dist/static/render.d.ts +12 -0
  51. package/dist/static/specs.d.ts +2 -0
  52. package/dist/static/types.d.ts +43 -0
  53. package/dist/tokens.css +322 -0
  54. package/dist/types/index.d.ts +36 -0
  55. package/dist/utils/slots.d.ts +6 -0
  56. package/docs/guides/installation.md +8 -2
  57. package/docs/guides/pure-css/_meta.yaml +8 -0
  58. package/docs/guides/pure-css/class-api.md +1070 -0
  59. package/docs/guides/pure-css/custom-elements.md +574 -0
  60. package/docs/guides/pure-css/index.md +86 -0
  61. package/docs/guides/pure-css/limitations.md +152 -0
  62. package/docs/guides/pure-css/static-helpers.md +1203 -0
  63. package/llms-full.txt +3739 -261
  64. package/package.json +19 -5
  65. package/src/components/Alert/SkAlert.vue +4 -2
  66. package/src/components/Breadcrumbs/SkBreadcrumbs.vue +6 -12
  67. package/src/components/Button/SkButton.vue +8 -5
  68. package/src/components/Card/SkCard.vue +13 -5
  69. package/src/components/Checkbox/SkCheckbox.vue +9 -2
  70. package/src/components/ContextMenu/SkContextMenuRadioGroup.vue +4 -1
  71. package/src/components/Dropdown/SkDropdown.vue +20 -3
  72. package/src/components/Dropdown/SkDropdownRadioGroup.vue +4 -1
  73. package/src/components/Dropdown/types.ts +2 -1
  74. package/src/components/Modal/SkModal.vue +11 -4
  75. package/src/components/NavBar/SkNavBar.vue +19 -8
  76. package/src/components/NavBar/context.ts +4 -2
  77. package/src/components/NavBar/types.ts +6 -1
  78. package/src/components/NumberInput/SkNumberInput.vue +10 -1
  79. package/src/components/Page/SkPage.vue +29 -15
  80. package/src/components/Panel/SkPanel.vue +2 -1
  81. package/src/components/Popover/SkPopover.vue +11 -4
  82. package/src/components/Radio/SkRadio.vue +9 -2
  83. package/src/components/ScrollArea/SkScrollArea.vue +78 -5
  84. package/src/components/Switch/SkSwitch.vue +14 -13
  85. package/src/components/Tabs/SkTab.vue +7 -2
  86. package/src/components/TreeView/SkTreeItem.vue +10 -2
  87. package/src/components/TreeView/SkTreeView.vue +7 -2
  88. package/src/composables/useCustomColors.ts +86 -77
  89. package/src/composables/usePortalContext.test.ts +0 -2
  90. package/src/shims.d.ts +10 -0
  91. package/src/static/__tests__/parity.test.ts +717 -0
  92. package/src/static/__tests__/parityHarness.test.ts +98 -0
  93. package/src/static/__tests__/parityHarness.ts +260 -0
  94. package/src/static/classes.test.ts +82 -0
  95. package/src/static/classes.ts +111 -0
  96. package/src/static/components/__tests__/helpers.test.ts +837 -0
  97. package/src/static/components/alert.ts +117 -0
  98. package/src/static/components/avatar.ts +86 -0
  99. package/src/static/components/breadcrumbs.ts +28 -0
  100. package/src/static/components/button.ts +75 -0
  101. package/src/static/components/card.ts +27 -0
  102. package/src/static/components/checkbox.ts +48 -0
  103. package/src/static/components/colorPicker.ts +45 -0
  104. package/src/static/components/divider.ts +39 -0
  105. package/src/static/components/dropdown.ts +36 -0
  106. package/src/static/components/field.ts +86 -0
  107. package/src/static/components/group.ts +27 -0
  108. package/src/static/components/input.ts +55 -0
  109. package/src/static/components/navBar.ts +94 -0
  110. package/src/static/components/numberInput.ts +64 -0
  111. package/src/static/components/page.ts +31 -0
  112. package/src/static/components/pagination.ts +27 -0
  113. package/src/static/components/panel.ts +33 -0
  114. package/src/static/components/progress.ts +31 -0
  115. package/src/static/components/radio.ts +53 -0
  116. package/src/static/components/select.ts +51 -0
  117. package/src/static/components/sidebar.ts +85 -0
  118. package/src/static/components/skeleton.ts +66 -0
  119. package/src/static/components/slider.ts +50 -0
  120. package/src/static/components/spinner.ts +94 -0
  121. package/src/static/components/switchInput.ts +49 -0
  122. package/src/static/components/table.ts +88 -0
  123. package/src/static/components/tag.ts +76 -0
  124. package/src/static/components/tagsInput.ts +35 -0
  125. package/src/static/components/textarea.ts +53 -0
  126. package/src/static/components/toolbar.ts +74 -0
  127. package/src/static/components/tooltip.ts +29 -0
  128. package/src/static/escape.test.ts +53 -0
  129. package/src/static/escape.ts +28 -0
  130. package/src/static/generated/defaults.ts +379 -0
  131. package/src/static/generated/propTypes.ts +426 -0
  132. package/src/static/index.ts +116 -0
  133. package/src/static/render.test.ts +83 -0
  134. package/src/static/render.ts +76 -0
  135. package/src/static/specs.test.ts +58 -0
  136. package/src/static/specs.ts +230 -0
  137. package/src/static/types.ts +176 -0
  138. package/src/styles/__tests__/testHelpers.ts +97 -0
  139. package/src/styles/base/_custom-elements.scss +51 -0
  140. package/src/styles/base/_index.scss +4 -0
  141. package/src/styles/components/__tests__/componentSelectors.test.ts +2575 -0
  142. package/src/styles/components/_alert.scss +82 -39
  143. package/src/styles/components/_avatar.scss +102 -47
  144. package/src/styles/components/_breadcrumbs.scss +39 -37
  145. package/src/styles/components/_button.scss +58 -5
  146. package/src/styles/components/_card.scss +64 -2
  147. package/src/styles/components/_checkbox.scss +35 -5
  148. package/src/styles/components/_color-picker.scss +48 -13
  149. package/src/styles/components/_divider.scss +86 -52
  150. package/src/styles/components/_dropdown.scss +214 -0
  151. package/src/styles/components/_field.scss +76 -23
  152. package/src/styles/components/_group.scss +190 -79
  153. package/src/styles/components/_index.scss +1 -0
  154. package/src/styles/components/_input.scss +81 -5
  155. package/src/styles/components/_menu.scss +1 -1
  156. package/src/styles/components/_navbar.scss +76 -45
  157. package/src/styles/components/_number-input.scss +98 -85
  158. package/src/styles/components/_page.scss +82 -23
  159. package/src/styles/components/_pagination.scss +240 -212
  160. package/src/styles/components/_panel.scss +268 -122
  161. package/src/styles/components/_progress.scss +120 -70
  162. package/src/styles/components/_radio.scss +35 -5
  163. package/src/styles/components/_scroll-area.scss +50 -22
  164. package/src/styles/components/_select.scss +40 -9
  165. package/src/styles/components/_sidebar.scss +59 -34
  166. package/src/styles/components/_skeleton.scss +111 -65
  167. package/src/styles/components/_slider.scss +34 -10
  168. package/src/styles/components/_spinner.scss +107 -56
  169. package/src/styles/components/_switch.scss +36 -5
  170. package/src/styles/components/_table.scss +150 -166
  171. package/src/styles/components/_tag.scss +244 -154
  172. package/src/styles/components/_tags-input.scss +46 -12
  173. package/src/styles/components/_textarea.scss +36 -5
  174. package/src/styles/components/_toolbar.scss +85 -31
  175. package/src/styles/components/_tooltip.scss +172 -3
  176. package/src/styles/mixins/_cut-border.scss +18 -4
  177. package/src/styles/mixins/_dual-selector.scss +192 -0
  178. package/src/styles/mixins/_index.scss +1 -0
  179. package/src/styles/mixins/dualSelector.test.ts +151 -0
  180. package/src/styles/themes/_colorful.scss +25 -0
  181. package/src/styles/themes/_greyscale.scss +25 -0
  182. package/src/styles/themes/_shade-scale.scss +39 -0
  183. package/src/styles/tokens/_semantic-color-kinds.scss +66 -0
  184. package/src/{types.ts → types/index.ts} +19 -11
  185. package/src/utils/slots.ts +75 -0
  186. package/web-types.json +980 -137
  187. package/dist/composables/useCustomColors.test.d.ts +0 -1
  188. package/dist/composables/useFocusTrap.test.d.ts +0 -1
  189. package/dist/composables/usePortalContext.test.d.ts +0 -1
  190. package/dist/styles/mixins/fluidSize.test.d.ts +0 -1
  191. package/dist/types.d.ts +0 -29
@@ -0,0 +1,58 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Spec Coverage Test
3
+ //
4
+ // Ensures every component directory under src/components/ has an entry in SPECS (or is
5
+ // explicitly noted as compound/hand-written). Catches drift when new Vue components are added.
6
+ //----------------------------------------------------------------------------------------------------------------------
7
+
8
+ import { describe, expect, it } from 'vitest';
9
+ import { readdirSync } from 'node:fs';
10
+ import { dirname, join } from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ import { SPECS } from './specs';
14
+
15
+ //----------------------------------------------------------------------------------------------------------------------
16
+
17
+ const here = dirname(fileURLToPath(import.meta.url));
18
+ const componentsDir = join(here, '..', 'components');
19
+
20
+ // Components that are out-of-scope per the design spec (Vue-only, not pure-CSS-eligible)
21
+ const OUT_OF_SCOPE = new Set([
22
+ 'Accordion',
23
+ 'Collapsible',
24
+ 'ContextMenu',
25
+ 'Listbox',
26
+ 'Modal',
27
+ 'Popover',
28
+ 'ScrollArea',
29
+ 'Splitter',
30
+ 'Tabs',
31
+ 'Theme',
32
+ 'Toast',
33
+ 'TreeView',
34
+ ]);
35
+
36
+ // Components with hand-written helpers (compound structure beyond the generic renderer's reach)
37
+ const HAND_WRITTEN = new Set([ 'Button', 'Checkbox', 'Radio', 'Switch', 'NumberInput', 'TagsInput' ]);
38
+
39
+ //----------------------------------------------------------------------------------------------------------------------
40
+
41
+ describe('spec coverage', () =>
42
+ {
43
+ const componentDirs = readdirSync(componentsDir, { withFileTypes: true })
44
+ .filter((entry) => entry.isDirectory())
45
+ .map((entry) => entry.name)
46
+ .filter((dir) => !OUT_OF_SCOPE.has(dir) && !HAND_WRITTEN.has(dir));
47
+
48
+ for(const dir of componentDirs)
49
+ {
50
+ it(`${ dir } has a spec`, () =>
51
+ {
52
+ const key = dir.charAt(0).toLowerCase() + dir.slice(1);
53
+ expect(SPECS[key], `SPECS["${ key }"] should exist for ${ dir }`).toBeDefined();
54
+ });
55
+ }
56
+ });
57
+
58
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,230 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Per-Component Spec Registry
3
+ //
4
+ // Maps each component name to its RenderSpec. Used by helper functions to render the right
5
+ // HTML structure with the right modifier-class composition and custom-color handling.
6
+ //
7
+ // Hand-maintained. When adding a component, add its spec here. The codegen covers prop types
8
+ // and defaults but not the render shape — that's this file's job.
9
+ //----------------------------------------------------------------------------------------------------------------------
10
+
11
+ import type { RenderSpec } from './render';
12
+
13
+ //----------------------------------------------------------------------------------------------------------------------
14
+
15
+ export const SPECS : Record<string, RenderSpec> = {
16
+ panel: {
17
+ tag: 'div',
18
+ classSpec: {
19
+ base: 'sk-panel',
20
+ kind: true,
21
+ size: true,
22
+ booleanFlags: [ 'no-border', 'no-decoration' ],
23
+ listFlags: [ { prop: 'corners', family: 'cut' } ],
24
+ singleChoiceFlags: [ { prop: 'decorationCorner', family: 'decoration' } ],
25
+ },
26
+ customColorVars: { base: '--sk-panel-color-base', text: '--sk-panel-fg' },
27
+ },
28
+
29
+ card: {
30
+ tag: 'div',
31
+ classSpec: { base: 'sk-card', kind: true },
32
+ customColorVars: { base: '--sk-panel-color-base', text: '--sk-panel-fg' },
33
+ },
34
+
35
+ alert: {
36
+ tag: 'div',
37
+ classSpec: {
38
+ base: 'sk-alert',
39
+ kind: true,
40
+ booleanFlags: [ 'subtle' ],
41
+ },
42
+ extraAttrs: { role: 'alert' },
43
+ customColorVars: { base: '--sk-alert-color-base', text: '--sk-alert-fg' },
44
+ },
45
+
46
+ divider: {
47
+ tag: 'div',
48
+ classSpec: {
49
+ base: 'sk-divider',
50
+ kind: true,
51
+ booleanFlags: [ 'subtle' ],
52
+ singleChoiceFlags: [ { prop: 'orientation', family: 'orientation' } ],
53
+ },
54
+ extraAttrs: { role: 'separator' },
55
+ },
56
+
57
+ page: {
58
+ tag: 'div',
59
+ classSpec: {
60
+ base: 'sk-page',
61
+ booleanFlags: [ 'flush', 'fixed-header', 'fixed-footer' ],
62
+ },
63
+ },
64
+
65
+ group: {
66
+ tag: 'div',
67
+ classSpec: {
68
+ base: 'sk-group',
69
+ singleChoiceFlags: [ { prop: 'orientation', family: 'orientation' } ],
70
+ },
71
+ },
72
+
73
+ // `prop: 'variant'` matches Vue's prop name; CSS family `shape` (sk-shape-*) is unchanged.
74
+ skeleton: {
75
+ tag: 'div',
76
+ classSpec: {
77
+ base: 'sk-skeleton',
78
+ singleChoiceFlags: [ { prop: 'variant', family: 'shape' } ],
79
+ booleanFlags: [ 'shimmer', 'pulse' ],
80
+ },
81
+ },
82
+
83
+ progress: {
84
+ tag: 'progress',
85
+ classSpec: {
86
+ base: 'sk-progress',
87
+ kind: true,
88
+ size: true,
89
+ booleanFlags: [ 'indeterminate' ],
90
+ },
91
+ void: true,
92
+ },
93
+
94
+ spinner: {
95
+ tag: 'div',
96
+ classSpec: { base: 'sk-spinner', kind: true, size: true },
97
+ },
98
+
99
+ // NavBar dir → camelCase key "navBar" (consistent with the test's key-derivation pattern)
100
+ navBar: {
101
+ tag: 'nav',
102
+ classSpec: { base: 'sk-navbar', kind: true, booleanFlags: [ 'sticky' ] },
103
+ },
104
+
105
+ toolbar: {
106
+ tag: 'div',
107
+ classSpec: {
108
+ base: 'sk-toolbar',
109
+ kind: true,
110
+ singleChoiceFlags: [ { prop: 'orientation', family: 'orientation' } ],
111
+ },
112
+ extraAttrs: { role: 'toolbar' },
113
+ },
114
+
115
+ sidebar: {
116
+ tag: 'aside',
117
+ classSpec: { base: 'sk-sidebar', kind: true, booleanFlags: [ 'dense' ] },
118
+ },
119
+
120
+ breadcrumbs: {
121
+ tag: 'nav',
122
+ classSpec: { base: 'sk-breadcrumbs', kind: true },
123
+ extraAttrs: { 'aria-label': 'Breadcrumbs' },
124
+ },
125
+
126
+ pagination: {
127
+ tag: 'nav',
128
+ classSpec: { base: 'sk-pagination', kind: true },
129
+ extraAttrs: { 'aria-label': 'Pagination' },
130
+ },
131
+
132
+ tag: {
133
+ tag: 'span',
134
+ classSpec: {
135
+ base: 'sk-tag',
136
+ kind: true,
137
+ size: true,
138
+ variant: true,
139
+ booleanFlags: [ 'removable' ],
140
+ },
141
+ },
142
+
143
+ avatar: {
144
+ tag: 'div',
145
+ classSpec: {
146
+ base: 'sk-avatar',
147
+ kind: true,
148
+ size: true,
149
+ singleChoiceFlags: [ { prop: 'shape', family: 'shape' } ],
150
+ },
151
+ },
152
+
153
+ field: {
154
+ tag: 'div',
155
+ classSpec: {
156
+ base: 'sk-field',
157
+ booleanFlags: [ 'has-error' ],
158
+ singleChoiceFlags: [ { prop: 'labelPosition', family: 'label-position' } ],
159
+ },
160
+ },
161
+
162
+ table: {
163
+ tag: 'table',
164
+ classSpec: {
165
+ base: 'sk-table',
166
+ kind: true,
167
+ variant: true,
168
+ booleanFlags: [ 'striped', 'bordered', 'compact', 'comfortable' ],
169
+ },
170
+ },
171
+
172
+ tooltip: {
173
+ tag: 'div',
174
+ classSpec: {
175
+ base: 'sk-tooltip',
176
+ kind: true,
177
+ variant: true,
178
+ singleChoiceFlags: [ { prop: 'placement', family: 'placement' } ],
179
+ },
180
+ extraAttrs: { role: 'tooltip' },
181
+ },
182
+
183
+ // Class-only form controls — class-API HTML on native elements
184
+ input: {
185
+ tag: 'input',
186
+ classSpec: { base: 'sk-input', kind: true, size: true },
187
+ void: true,
188
+ },
189
+
190
+ textarea: {
191
+ tag: 'textarea',
192
+ classSpec: { base: 'sk-textarea', kind: true, size: true },
193
+ },
194
+
195
+ select: {
196
+ tag: 'select',
197
+ classSpec: { base: 'sk-select', kind: true, size: true },
198
+ },
199
+
200
+ slider: {
201
+ tag: 'input',
202
+ classSpec: {
203
+ base: 'sk-slider',
204
+ kind: true,
205
+ size: true,
206
+ singleChoiceFlags: [ { prop: 'orientation', family: 'orientation' } ],
207
+ },
208
+ void: true,
209
+ extraAttrs: { type: 'range' },
210
+ },
211
+
212
+ colorPicker: {
213
+ tag: 'input',
214
+ classSpec: { base: 'sk-color-picker', size: true },
215
+ void: true,
216
+ extraAttrs: { type: 'color' },
217
+ },
218
+
219
+ // Compound components (checkbox/radio/switch/numberInput/tagsInput) are hand-written — see
220
+ // respective component files. They compose multiple inner elements the generic renderer
221
+ // can't express.
222
+
223
+ // Dropdown: disclosure via <details> + <summary>
224
+ dropdown: {
225
+ tag: 'details',
226
+ classSpec: { base: 'sk-dropdown', kind: true, size: true },
227
+ },
228
+ };
229
+
230
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,176 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Static Helpers — Shared Types
3
+ //
4
+ // All types are declared directly here — no imports from outside `src/static/`. This keeps
5
+ // TypeScript's computed common source root at `src/static/` so vite-plugin-dts emits
6
+ // declarations directly to `dist/static/` without any post-build file shuffling.
7
+ //
8
+ // The token types (SemanticKind, ColorKind, ComponentKind, ComponentSize, ComponentVariant) are
9
+ // copied from `src/types.ts`. They must be kept in sync manually if the canonical definitions
10
+ // change. Static module consumers can import these types from `@skewedaspect/sleekspace-ui/static`
11
+ // without pulling in the Vue package.
12
+ //----------------------------------------------------------------------------------------------------------------------
13
+
14
+ // Semantic kinds (themeable, colors with meaning)
15
+ export type SemanticKind = 'neutral' | 'primary' | 'accent' | 'danger' | 'info' | 'success' | 'warning';
16
+
17
+ // Color kinds (non-themeable, direct palette access)
18
+ export type ColorKind = 'boulder' | 'neon-blue' | 'light-blue' | 'neon-orange' | 'neon-purple' | 'neon-green'
19
+ | 'neon-mint' | 'neon-pink' | 'yellow' | 'red';
20
+
21
+ // All component kinds (semantic + color)
22
+ export type ComponentKind = SemanticKind | ColorKind;
23
+
24
+ // Common component sizes
25
+ export type ComponentSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
26
+
27
+ // Common button/interactive variants
28
+ export type ComponentVariant = 'solid' | 'outline' | 'subtle' | 'ghost' | 'link';
29
+
30
+ //----------------------------------------------------------------------------------------------------------------------
31
+ // Custom-color props (mirror of ComponentCustomColors from src/types.ts)
32
+ //
33
+ // Defined directly so the `./static` subpath doesn't pull in Vue-adjacent code.
34
+ //----------------------------------------------------------------------------------------------------------------------
35
+
36
+ export interface StaticCustomColors
37
+ {
38
+ baseColor ?: string;
39
+ textColor ?: string;
40
+ }
41
+
42
+ //----------------------------------------------------------------------------------------------------------------------
43
+ // Common helper signature
44
+ //----------------------------------------------------------------------------------------------------------------------
45
+
46
+ export type HtmlString = string;
47
+
48
+ //----------------------------------------------------------------------------------------------------------------------
49
+ // Alert
50
+ //----------------------------------------------------------------------------------------------------------------------
51
+
52
+ export type AlertKind = ComponentKind;
53
+
54
+ //----------------------------------------------------------------------------------------------------------------------
55
+ // Avatar
56
+ //----------------------------------------------------------------------------------------------------------------------
57
+
58
+ export type AvatarSize = ComponentSize;
59
+
60
+ //----------------------------------------------------------------------------------------------------------------------
61
+ // Breadcrumbs
62
+ //----------------------------------------------------------------------------------------------------------------------
63
+
64
+ export type BreadcrumbsKind = ComponentKind;
65
+
66
+ //----------------------------------------------------------------------------------------------------------------------
67
+ // Button
68
+ //----------------------------------------------------------------------------------------------------------------------
69
+
70
+ export type ButtonKind = ComponentKind;
71
+ export type ButtonSize = ComponentSize;
72
+ export type ButtonVariant = ComponentVariant;
73
+ export type ButtonType = 'button' | 'submit' | 'reset';
74
+
75
+ //----------------------------------------------------------------------------------------------------------------------
76
+ // Card
77
+ //----------------------------------------------------------------------------------------------------------------------
78
+
79
+ export type CardKind = ComponentKind;
80
+
81
+ //----------------------------------------------------------------------------------------------------------------------
82
+ // Divider
83
+ //----------------------------------------------------------------------------------------------------------------------
84
+
85
+ export type DividerOrientation = 'horizontal' | 'vertical';
86
+ export type DividerVariant = 'subtle';
87
+
88
+ //----------------------------------------------------------------------------------------------------------------------
89
+ // Field
90
+ //----------------------------------------------------------------------------------------------------------------------
91
+
92
+ export type FieldLabelPosition = 'top' | 'left';
93
+
94
+ //----------------------------------------------------------------------------------------------------------------------
95
+ // Group
96
+ //----------------------------------------------------------------------------------------------------------------------
97
+
98
+ export type GroupOrientation = 'horizontal' | 'vertical';
99
+
100
+ //----------------------------------------------------------------------------------------------------------------------
101
+ // NavBar
102
+ //----------------------------------------------------------------------------------------------------------------------
103
+
104
+ export type NavBarKind = ComponentKind;
105
+
106
+ //----------------------------------------------------------------------------------------------------------------------
107
+ // Page
108
+ //----------------------------------------------------------------------------------------------------------------------
109
+
110
+ export type PagePanelMode = 'auto' | 'persistent' | 'drawer';
111
+
112
+ //----------------------------------------------------------------------------------------------------------------------
113
+ // Pagination
114
+ //----------------------------------------------------------------------------------------------------------------------
115
+
116
+ export type PaginationKind = ComponentKind;
117
+
118
+ //----------------------------------------------------------------------------------------------------------------------
119
+ // Panel
120
+ //----------------------------------------------------------------------------------------------------------------------
121
+
122
+ export type PanelKind = ComponentKind;
123
+ export type PanelSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
124
+ export type PanelCorner = 'top-left' | 'top-right' | 'bottom-right' | 'bottom-left';
125
+
126
+ //----------------------------------------------------------------------------------------------------------------------
127
+ // Progress
128
+ //----------------------------------------------------------------------------------------------------------------------
129
+
130
+ export type ProgressSize = ComponentSize;
131
+
132
+ //----------------------------------------------------------------------------------------------------------------------
133
+ // Sidebar
134
+ //----------------------------------------------------------------------------------------------------------------------
135
+
136
+ export type SidebarKind = ComponentKind;
137
+ export type SidebarSide = 'left' | 'right';
138
+
139
+ //----------------------------------------------------------------------------------------------------------------------
140
+ // Spinner
141
+ //----------------------------------------------------------------------------------------------------------------------
142
+
143
+ export type SpinnerSize = ComponentSize;
144
+ export type SpinnerVariant = 'circular' | 'dots' | 'crosshair';
145
+
146
+ //----------------------------------------------------------------------------------------------------------------------
147
+ // Table
148
+ //----------------------------------------------------------------------------------------------------------------------
149
+
150
+ export type TableKind = ComponentKind;
151
+ export type TableVariant = 'default' | 'compact' | 'comfortable';
152
+
153
+ //----------------------------------------------------------------------------------------------------------------------
154
+ // Tag
155
+ //----------------------------------------------------------------------------------------------------------------------
156
+
157
+ export type TagVariant = 'solid' | 'outline' | 'subtle' | 'ghost';
158
+ export type TagSize = 'sm' | 'md' | 'lg' | 'xl';
159
+
160
+ //----------------------------------------------------------------------------------------------------------------------
161
+ // Toolbar
162
+ //----------------------------------------------------------------------------------------------------------------------
163
+
164
+ export type ToolbarKind = ComponentKind;
165
+ export type ToolbarOrientation = 'horizontal' | 'vertical';
166
+ export type ToolbarCorner = 'top-left' | 'top-right' | 'bottom-right' | 'bottom-left';
167
+
168
+ //----------------------------------------------------------------------------------------------------------------------
169
+ // Tooltip
170
+ //----------------------------------------------------------------------------------------------------------------------
171
+
172
+ export type TooltipKind = ComponentKind;
173
+ export type TooltipVariant = ComponentVariant;
174
+ export type TooltipSide = 'top' | 'right' | 'bottom' | 'left';
175
+
176
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,97 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Shared SCSS Test Helpers
3
+ //
4
+ // Parses the canonical kinds/sizes/variants lists from `src/styles/theme/_variables.scss` so
5
+ // test files can iterate over them without hardcoding. Also exports a small regex helper for
6
+ // matching Sass's unquoted attribute values (Sass strips quotes from attribute selectors when
7
+ // CSS allows it, so `[kind="primary"]` can be emitted as `[kind=primary]`).
8
+ //----------------------------------------------------------------------------------------------------------------------
9
+
10
+ import { readFileSync } from 'node:fs';
11
+ import { dirname, resolve } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ //----------------------------------------------------------------------------------------------------------------------
15
+
16
+ const here = dirname(fileURLToPath(import.meta.url));
17
+ const variablesPath = resolve(here, '..', 'theme', '_variables.scss');
18
+ const variablesSource = readFileSync(variablesPath, 'utf8');
19
+
20
+ function extractList(name : string) : string[]
21
+ {
22
+ const pattern = new RegExp(`\\$${ name }\\s*:\\s*([^;]+);`, 'm');
23
+ const match = variablesSource.match(pattern);
24
+ if(!match)
25
+ {
26
+ throw new Error(`Could not find $${ name } in ${ variablesPath }`);
27
+ }
28
+ return match[1]
29
+ .split(',')
30
+ .map((entry) => entry.trim())
31
+ .filter(Boolean);
32
+ }
33
+
34
+ //----------------------------------------------------------------------------------------------------------------------
35
+
36
+ export const SEMANTIC_KINDS : readonly string[] = extractList('semantic-kinds');
37
+ export const COLOR_KINDS : readonly string[] = extractList('color-kinds');
38
+ export const KINDS : readonly string[] = extractList('kinds');
39
+ export const VARIANTS : readonly string[] = extractList('variants');
40
+ export const SIZES : readonly string[] = extractList('sizes');
41
+
42
+ //----------------------------------------------------------------------------------------------------------------------
43
+ // Regex escape helpers
44
+ //
45
+ // `escapeRe` escapes hyphens in a value so it slots into a regex char class safely (only
46
+ // hyphens matter for the kinds/sizes/modifiers the selector tests assert on). `attrValueRe`
47
+ // wraps the escaped value in optional quotes so it matches both Sass's quoted and unquoted
48
+ // attribute-selector emissions. Use both inside `new RegExp(...)` template literals to keep
49
+ // dual-selector assertions short.
50
+ //----------------------------------------------------------------------------------------------------------------------
51
+
52
+ export function escapeRe(value : string) : string
53
+ {
54
+ return value.replace(/-/g, '\\-');
55
+ }
56
+
57
+ export function attrValueRe(value : string) : string
58
+ {
59
+ return `"?${ escapeRe(value) }"?`;
60
+ }
61
+
62
+ //----------------------------------------------------------------------------------------------------------------------
63
+ // Dual-selector regex builder
64
+ //
65
+ // Builds the regex fragment matching a dual-selector pair of the form:
66
+ // .sk-<component>.sk-<classModifier><suffix>, sk-<component>[<attr><op><attrValue>]<suffix>
67
+ // Pass `attrValue` undefined for boolean attributes (just `[attr]`).
68
+ //----------------------------------------------------------------------------------------------------------------------
69
+
70
+ export interface DualSelectorOptions
71
+ {
72
+ attrOp ?: string;
73
+ suffix ?: string;
74
+ // Regex fragment appended after `\s+` to both the class and attribute forms (e.g. for
75
+ // a descendant combinator like `\.sk-page-header`).
76
+ descendant ?: string;
77
+ }
78
+
79
+ export function dualSelectorRe(
80
+ component : string,
81
+ classModifier : string,
82
+ attr : string,
83
+ attrValue ?: string,
84
+ opts : DualSelectorOptions = {}
85
+ ) : string
86
+ {
87
+ const op = opts.attrOp ?? '=';
88
+ const suffix = opts.suffix ?? '';
89
+ const tail = opts.descendant ? `${ suffix }\\s+${ opts.descendant }` : suffix;
90
+ const cls = `\\.sk-${ component }\\.sk-${ escapeRe(classModifier) }${ tail }`;
91
+ const attrFrag = attrValue === undefined
92
+ ? `sk-${ component }\\[${ attr }\\]${ tail }`
93
+ : `sk-${ component }\\[${ attr }${ op }${ attrValueRe(attrValue) }\\]${ tail }`;
94
+ return `${ cls },\\s*${ attrFrag }`;
95
+ }
96
+
97
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,51 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Custom Element Base Styles
3
+ //
4
+ // Unknown elements default to `display: inline` in HTML. For the light-DOM custom-element forms
5
+ // of in-scope container components (`<sk-panel>`, `<sk-card>`, etc.), we want `display: block`
6
+ // (or an appropriate alternative) so they behave like their `<div>`-based class-API
7
+ // counterparts without authors having to add `display:block` themselves.
8
+ //
9
+ // This file ONLY applies to custom-element TAG names. The class API (`<div class="sk-panel">`)
10
+ // inherits display from the underlying element and is unaffected.
11
+ //----------------------------------------------------------------------------------------------------------------------
12
+
13
+ @layer base
14
+ {
15
+ // Block-level containers + separators
16
+ sk-panel,
17
+ sk-card,
18
+ sk-alert,
19
+ sk-page,
20
+ sk-navbar,
21
+ sk-toolbar,
22
+ sk-sidebar,
23
+ sk-breadcrumbs,
24
+ sk-pagination,
25
+ sk-field,
26
+ sk-table,
27
+ sk-progress,
28
+ sk-divider
29
+ {
30
+ display: block;
31
+ }
32
+
33
+ // Inline-flex containers (layout helpers, badges)
34
+ sk-group,
35
+ sk-tag,
36
+ sk-avatar,
37
+ sk-spinner,
38
+ sk-skeleton
39
+ {
40
+ display: inline-flex;
41
+ }
42
+
43
+ // Tooltip container — positioning handled by the component's CSS; the tag itself is a wrapper
44
+ sk-tooltip
45
+ {
46
+ display: inline-block;
47
+ position: relative;
48
+ }
49
+ }
50
+
51
+ //----------------------------------------------------------------------------------------------------------------------
@@ -19,6 +19,10 @@
19
19
 
20
20
  // 4. Typography
21
21
  @include meta.load-css('typography');
22
+
23
+ // 5. Custom Element Display Defaults (must come after reset so the reset's
24
+ // element-level `display: inline` defaults don't override these rules)
25
+ @include meta.load-css('custom-elements');
22
26
  }
23
27
 
24
28
  //----------------------------------------------------------------------------------------------------------------------