@kenlo/sds-design-system 0.9.9

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 (246) hide show
  1. package/README.md +129 -0
  2. package/components/Accordion/Accordion.css +186 -0
  3. package/components/Accordion/accordion-variants.js +17 -0
  4. package/components/Accordion/accordion.marko +264 -0
  5. package/components/AccordionGroup/AccordionGroup.css +35 -0
  6. package/components/AccordionGroup/accordion-group.marko +17 -0
  7. package/components/Alert/Alert.css +104 -0
  8. package/components/Alert/alert-variants.js +35 -0
  9. package/components/Alert/alert.marko +94 -0
  10. package/components/AttachmentList/AttachmentList.css +92 -0
  11. package/components/AttachmentList/attachment-list.marko +51 -0
  12. package/components/AttachmentList/attachment-row.marko +167 -0
  13. package/components/Avatar/Avatar.css +110 -0
  14. package/components/Avatar/avatar-variants.js +12 -0
  15. package/components/Avatar/avatar.marko +71 -0
  16. package/components/Badge/Badge.css +381 -0
  17. package/components/Badge/badge-dismissible.marko +143 -0
  18. package/components/Badge/badge-variants.js +35 -0
  19. package/components/Badge/badge.marko +134 -0
  20. package/components/BottomSheet/BottomSheet.css +300 -0
  21. package/components/BottomSheet/bottom-sheet.marko +340 -0
  22. package/components/BulkActionBar/BulkActionBar.css +88 -0
  23. package/components/BulkActionBar/bulk-action-bar.marko +186 -0
  24. package/components/Button/Button.css +340 -0
  25. package/components/Button/button-variants.js +25 -0
  26. package/components/Button/button.marko +139 -0
  27. package/components/Card/Card.css +391 -0
  28. package/components/Card/card-body.marko +5 -0
  29. package/components/Card/card-collapsible.marko +94 -0
  30. package/components/Card/card-footer.marko +12 -0
  31. package/components/Card/card-header.marko +30 -0
  32. package/components/Card/card-info-pair.marko +17 -0
  33. package/components/Card/card-info-row.marko +12 -0
  34. package/components/Card/card-section.marko +14 -0
  35. package/components/Card/card-variants.js +24 -0
  36. package/components/Card/card.marko +124 -0
  37. package/components/Checkbox/Checkbox.css +249 -0
  38. package/components/Checkbox/checkbox-variants.js +21 -0
  39. package/components/Checkbox/checkbox.marko +128 -0
  40. package/components/ColorPicker/ColorPicker.css +253 -0
  41. package/components/ColorPicker/color-maths.js +104 -0
  42. package/components/ColorPicker/color-picker.marko +474 -0
  43. package/components/DatePicker/DatePicker.css +429 -0
  44. package/components/DatePicker/date-logic.js +225 -0
  45. package/components/DatePicker/date-picker.marko +492 -0
  46. package/components/Drawer/Drawer.css +174 -0
  47. package/components/Drawer/drawer.marko +262 -0
  48. package/components/Floating/Floating.css +28 -0
  49. package/components/Floating/floating-position.js +251 -0
  50. package/components/Floating/floating.marko +291 -0
  51. package/components/Form/Form.css +15 -0
  52. package/components/Form/form-context.js +69 -0
  53. package/components/Form/form.marko +73 -0
  54. package/components/GradientPicker/GradientPicker.css +179 -0
  55. package/components/GradientPicker/gradient-picker.marko +398 -0
  56. package/components/Grid/Grid.css +161 -0
  57. package/components/Grid/col.marko +36 -0
  58. package/components/Grid/grid-variants.js +43 -0
  59. package/components/Grid/grid.marko +25 -0
  60. package/components/Header/Header.css +457 -0
  61. package/components/Header/header.marko +523 -0
  62. package/components/Icon/Icon.css +44 -0
  63. package/components/Icon/icon-colors.js +31 -0
  64. package/components/Icon/icon.marko +56 -0
  65. package/components/IconButton/IconButton.css +85 -0
  66. package/components/IconButton/icon-button-variants.js +18 -0
  67. package/components/IconButton/icon-button.marko +103 -0
  68. package/components/Incrementer/Incrementer.css +94 -0
  69. package/components/Incrementer/incrementer.marko +70 -0
  70. package/components/Input/Input.css +547 -0
  71. package/components/Input/input-countries.js +109 -0
  72. package/components/Input/input-masks.js +218 -0
  73. package/components/Input/input-types.js +194 -0
  74. package/components/Input/input.marko +530 -0
  75. package/components/Menu/Menu.css +651 -0
  76. package/components/Menu/menu-variants.js +89 -0
  77. package/components/Menu/menu.marko +560 -0
  78. package/components/Modal/Modal.css +160 -0
  79. package/components/Modal/modal.marko +296 -0
  80. package/components/Nav/Nav.css +298 -0
  81. package/components/Nav/nav-variants.js +23 -0
  82. package/components/Nav/nav.marko +164 -0
  83. package/components/Notification/Notification.css +78 -0
  84. package/components/Notification/notification-variants.js +30 -0
  85. package/components/Notification/notification.marko +55 -0
  86. package/components/Pagination/Pagination.css +78 -0
  87. package/components/Pagination/pagination-pages.js +66 -0
  88. package/components/Pagination/pagination.marko +141 -0
  89. package/components/Pill/Pill.css +193 -0
  90. package/components/Pill/pill-variants.js +16 -0
  91. package/components/Pill/pill.marko +165 -0
  92. package/components/Popover/Popover.css +121 -0
  93. package/components/Popover/popover.marko +96 -0
  94. package/components/Radio/Radio.css +230 -0
  95. package/components/Radio/radio-variants.js +16 -0
  96. package/components/Radio/radio.marko +93 -0
  97. package/components/RadioGroup/RadioGroup.css +32 -0
  98. package/components/RadioGroup/radio-group.marko +93 -0
  99. package/components/SearchBar/SearchBar.css +181 -0
  100. package/components/SearchBar/search-bar-variants.js +24 -0
  101. package/components/SearchBar/search-bar.marko +157 -0
  102. package/components/Select/Select.css +427 -0
  103. package/components/Select/select-options.js +163 -0
  104. package/components/Select/select.marko +449 -0
  105. package/components/Separator/Separator.css +22 -0
  106. package/components/Separator/separator.marko +14 -0
  107. package/components/Skeleton/Skeleton.css +100 -0
  108. package/components/Skeleton/skeleton-avatar.marko +28 -0
  109. package/components/Skeleton/skeleton-button.marko +22 -0
  110. package/components/Skeleton/skeleton-image.marko +20 -0
  111. package/components/Skeleton/skeleton-input.marko +22 -0
  112. package/components/Skeleton/skeleton-variants.js +16 -0
  113. package/components/Skeleton/skeleton.marko +53 -0
  114. package/components/Spinner/Spinner.css +173 -0
  115. package/components/Spinner/spinner-geometry.js +39 -0
  116. package/components/Spinner/spinner-overlay.marko +97 -0
  117. package/components/Spinner/spinner.marko +88 -0
  118. package/components/StatCard/StatCard.css +90 -0
  119. package/components/StatCard/stat-card-group.marko +5 -0
  120. package/components/StatCard/stat-card.marko +57 -0
  121. package/components/Switch/Switch.css +209 -0
  122. package/components/Switch/switch.marko +99 -0
  123. package/components/Table/Table.css +270 -0
  124. package/components/Table/table-cell.marko +28 -0
  125. package/components/Table/table-header.marko +27 -0
  126. package/components/Table/table-logic.js +129 -0
  127. package/components/Table/table.marko +372 -0
  128. package/components/Text/Text.css +79 -0
  129. package/components/Text/text-variants.js +25 -0
  130. package/components/Text/text.marko +48 -0
  131. package/components/TextEditor/TextEditor.css +78 -0
  132. package/components/TextEditor/safe-url.js +50 -0
  133. package/components/TextEditor/text-editor-commands.js +22 -0
  134. package/components/TextEditor/text-editor.marko +253 -0
  135. package/components/Toast/Toast.css +88 -0
  136. package/components/Toast/toast-api.js +19 -0
  137. package/components/Toast/toast-store.js +118 -0
  138. package/components/Toast/toast-variants.js +16 -0
  139. package/components/Toast/toast.marko +93 -0
  140. package/components/Toast/toaster.marko +70 -0
  141. package/components/Toggle/Toggle.css +169 -0
  142. package/components/Toggle/toggle.marko +93 -0
  143. package/components/Tooltip/Tooltip.css +115 -0
  144. package/components/Tooltip/tooltip.marko +77 -0
  145. package/components/Uploader/Uploader.css +180 -0
  146. package/components/Uploader/crop-geometry.js +161 -0
  147. package/components/Uploader/uploader.marko +269 -0
  148. package/foundations/Scrollbar/Scrollbar.css +26 -0
  149. package/foundations/attrs.js +27 -0
  150. package/foundations/breakpoints.js +27 -0
  151. package/foundations/contrast.js +74 -0
  152. package/foundations/custom-scheme.js +222 -0
  153. package/foundations/overlay.js +108 -0
  154. package/foundations/styles.css +284 -0
  155. package/foundations/text.js +22 -0
  156. package/foundations/theme-server.js +81 -0
  157. package/foundations/theme.js +42 -0
  158. package/foundations/tokens.css +1656 -0
  159. package/icons/custom/custom-video-horizontal-filled.marko +6 -0
  160. package/icons/custom/custom-video-horizontal-regular.marko +6 -0
  161. package/icons/custom/custom-video-tour-3d-filled.marko +7 -0
  162. package/icons/custom/custom-video-tour-3d-regular.marko +9 -0
  163. package/icons/custom/custom-video-vertical-filled.marko +6 -0
  164. package/icons/custom/custom-video-vertical-regular.marko +6 -0
  165. package/icons/generated/fluent-add-regular.marko +3 -0
  166. package/icons/generated/fluent-alert-regular.marko +3 -0
  167. package/icons/generated/fluent-apps-regular.marko +3 -0
  168. package/icons/generated/fluent-arrow-download-regular.marko +3 -0
  169. package/icons/generated/fluent-arrow-left-regular.marko +3 -0
  170. package/icons/generated/fluent-arrow-right-regular.marko +3 -0
  171. package/icons/generated/fluent-arrow-rotate-clockwise-regular.marko +3 -0
  172. package/icons/generated/fluent-arrow-rotate-counterclockwise-regular.marko +3 -0
  173. package/icons/generated/fluent-border-all-regular.marko +3 -0
  174. package/icons/generated/fluent-calendar-regular.marko +3 -0
  175. package/icons/generated/fluent-checkmark-circle-filled.marko +3 -0
  176. package/icons/generated/fluent-checkmark-regular.marko +3 -0
  177. package/icons/generated/fluent-chevron-down-regular.marko +3 -0
  178. package/icons/generated/fluent-chevron-left-regular.marko +3 -0
  179. package/icons/generated/fluent-chevron-right-regular.marko +3 -0
  180. package/icons/generated/fluent-chevron-up-regular.marko +3 -0
  181. package/icons/generated/fluent-circle-half-fill-regular.marko +3 -0
  182. package/icons/generated/fluent-clock-regular.marko +3 -0
  183. package/icons/generated/fluent-color-regular.marko +3 -0
  184. package/icons/generated/fluent-crop-regular.marko +3 -0
  185. package/icons/generated/fluent-dark-theme-regular.marko +3 -0
  186. package/icons/generated/fluent-delete-regular.marko +3 -0
  187. package/icons/generated/fluent-desktop-regular.marko +3 -0
  188. package/icons/generated/fluent-dismiss-circle-filled.marko +3 -0
  189. package/icons/generated/fluent-dismiss-regular.marko +3 -0
  190. package/icons/generated/fluent-document-pdf-regular.marko +3 -0
  191. package/icons/generated/fluent-document-regular.marko +3 -0
  192. package/icons/generated/fluent-eye-off-regular.marko +3 -0
  193. package/icons/generated/fluent-eye-regular.marko +3 -0
  194. package/icons/generated/fluent-eyedropper-regular.marko +3 -0
  195. package/icons/generated/fluent-grid-regular.marko +3 -0
  196. package/icons/generated/fluent-history-regular.marko +3 -0
  197. package/icons/generated/fluent-home-regular.marko +3 -0
  198. package/icons/generated/fluent-image-regular.marko +3 -0
  199. package/icons/generated/fluent-info-regular.marko +3 -0
  200. package/icons/generated/fluent-link-regular.marko +3 -0
  201. package/icons/generated/fluent-more-horizontal-regular.marko +3 -0
  202. package/icons/generated/fluent-navigation-regular.marko +3 -0
  203. package/icons/generated/fluent-paint-brush-regular.marko +3 -0
  204. package/icons/generated/fluent-question-circle-regular.marko +3 -0
  205. package/icons/generated/fluent-search-regular.marko +3 -0
  206. package/icons/generated/fluent-settings-regular.marko +3 -0
  207. package/icons/generated/fluent-square-hint-regular.marko +3 -0
  208. package/icons/generated/fluent-subtract-regular.marko +3 -0
  209. package/icons/generated/fluent-text-align-center-regular.marko +3 -0
  210. package/icons/generated/fluent-text-align-left-regular.marko +3 -0
  211. package/icons/generated/fluent-text-align-right-regular.marko +3 -0
  212. package/icons/generated/fluent-text-bold-regular.marko +3 -0
  213. package/icons/generated/fluent-text-bullet-list-regular.marko +3 -0
  214. package/icons/generated/fluent-text-font-regular.marko +3 -0
  215. package/icons/generated/fluent-text-font-size-regular.marko +3 -0
  216. package/icons/generated/fluent-text-italic-regular.marko +3 -0
  217. package/icons/generated/fluent-text-number-list-ltr-regular.marko +3 -0
  218. package/icons/generated/fluent-video-regular.marko +3 -0
  219. package/icons/generated/fluent-warning-filled.marko +3 -0
  220. package/icons/generated/fluent-weather-moon-regular.marko +3 -0
  221. package/icons/generated/fluent-weather-sunny-regular.marko +3 -0
  222. package/icons/generated/fluent-zoom-in-regular.marko +3 -0
  223. package/icons/generated/fluent-zoom-out-regular.marko +3 -0
  224. package/icons/manifest.json +61 -0
  225. package/icons/social/social-facebook-color.marko +7 -0
  226. package/icons/social/social-facebook.marko +6 -0
  227. package/icons/social/social-google-color.marko +20 -0
  228. package/icons/social/social-google.marko +6 -0
  229. package/icons/social/social-instagram-color.marko +16 -0
  230. package/icons/social/social-instagram.marko +6 -0
  231. package/icons/social/social-linkedin-color.marko +8 -0
  232. package/icons/social/social-linkedin.marko +8 -0
  233. package/icons/social/social-messenger-color.marko +15 -0
  234. package/icons/social/social-messenger.marko +6 -0
  235. package/icons/social/social-pinterest-color.marko +6 -0
  236. package/icons/social/social-pinterest.marko +6 -0
  237. package/icons/social/social-telegram-color.marko +7 -0
  238. package/icons/social/social-telegram.marko +6 -0
  239. package/icons/social/social-whatsapp-color.marko +8 -0
  240. package/icons/social/social-whatsapp.marko +7 -0
  241. package/icons/social/social-x-color.marko +7 -0
  242. package/icons/social/social-x.marko +6 -0
  243. package/icons/social/social-youtube-color.marko +7 -0
  244. package/icons/social/social-youtube.marko +6 -0
  245. package/marko.json +203 -0
  246. package/package.json +55 -0
@@ -0,0 +1,340 @@
1
+ /* SDS Button.
2
+ Ported from kdls-design-system v2.5.1 (0f3edf8) components/Button/Button.css
3
+ by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
4
+
5
+ Rules removed with the brand families they targeted:
6
+ [data-color-scheme="lya"] .sds-button--primary
7
+ [data-color-scheme="lya"] .sds-button--primary:hover:not(:disabled):not(.sds-button--loading)
8
+ [data-color-scheme="lya"] .sds-button--primary:active:not(:disabled):not(.sds-button--loading)
9
+ [data-color-scheme="lya"] .sds-button--primary:focus-visible
10
+ [data-color-scheme="lya"] .sds-button--ghost
11
+ [data-color-scheme="lya"] .sds-button--ghost:hover:not(:disabled):not(.sds-button--loading)
12
+ [data-color-scheme="lya"] .sds-button--ghost:active:not(:disabled):not(.sds-button--loading)
13
+ [data-color-scheme="lya"] .sds-button--ghost:focus-visible
14
+ [data-color-scheme="lya"] .sds-button--pill:hover:not(:disabled):not(.sds-button--loading)
15
+ [data-color-scheme="lya"] .sds-button--pillFilled
16
+ [data-color-scheme="lya"] .sds-button--pillFilled:hover:not(:disabled):not(.sds-button--loading)
17
+ [data-color-scheme="lya"] .sds-button--pillFilled:active:not(:disabled):not(.sds-button--loading)
18
+ [data-color-scheme="lya"] .sds-button--pillFilled:focus-visible
19
+ [data-color-scheme="lya"] .sds-button--link
20
+ [data-color-scheme="lya"] .sds-button--link:hover:not(:disabled):not(.sds-button--loading)
21
+ [data-color-scheme="lya"] .sds-button--link:active:not(:disabled):not(.sds-button--loading)
22
+ *//* ============================================================
23
+ SDS Button
24
+ Variants: primary (filled) | ghost (outlined) | link
25
+ Sizes: small (48px) | medium (48px) | large (56px)
26
+ link: 24px fixed
27
+ Color: follows global data-color-scheme on root element
28
+ (blue by default, override via data-color-scheme)
29
+ States: enabled | hover | pressed | focused | disabled
30
+ ============================================================ */
31
+
32
+ .sds-button {
33
+ display: inline-flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ box-sizing: border-box;
37
+ border: 2px solid transparent;
38
+ cursor: pointer;
39
+ font-family: var(--font-family-base);
40
+ font-weight: var(--font-weight-bold);
41
+ text-decoration: none;
42
+ white-space: nowrap;
43
+ transition:
44
+ background-color 120ms ease,
45
+ color 120ms ease,
46
+ border-color 120ms ease,
47
+ box-shadow 120ms ease,
48
+ opacity 120ms ease;
49
+ outline: none;
50
+ position: relative;
51
+ letter-spacing: -0.01em;
52
+ -webkit-font-smoothing: antialiased;
53
+ }
54
+ .sds-button--full {
55
+ width: 100%;
56
+ }
57
+ .sds-button__tooltip-wrapper--full {
58
+ display: flex;
59
+ width: 100%;
60
+ }
61
+ /* ──────────────────────────────────────────────
62
+ SIZES
63
+ ────────────────────────────────────────────── */
64
+ .sds-button--md {
65
+ height: 48px;
66
+ padding: 0 var(--spacing-5);
67
+ gap: var(--spacing-2);
68
+ font-size: var(--font-size-title-sm);
69
+ line-height: var(--lh-title-sm);
70
+ /* soft, the install default — the four schemes are at the end of the file */
71
+ border-radius: 13px;
72
+ }
73
+ .sds-button--lg {
74
+ height: 56px;
75
+ padding: 0 var(--spacing-6);
76
+ gap: var(--spacing-2);
77
+ font-size: var(--font-size-title-sm);
78
+ line-height: var(--lh-title-sm);
79
+ /* soft, the install default — the four schemes are at the end of the file */
80
+ border-radius: 15px;
81
+ }
82
+ /* ──────────────────────────────────────────────
83
+ ICONS
84
+ ────────────────────────────────────────────── */
85
+ .sds-button__icon {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ flex-shrink: 0;
89
+ font-size: 24px;
90
+ width: 24px;
91
+ height: 24px;
92
+ }
93
+ /* ──────────────────────────────────────────────
94
+ VARIANT: primary (filled)
95
+ Uses --color-primary-* → follows data-color-scheme
96
+ ────────────────────────────────────────────── */
97
+ .sds-button--primary {
98
+ background: var(--color-primary-50);
99
+ color: var(--color-on-primary);
100
+ }
101
+ .sds-button--primary:hover:not(:disabled):not(.sds-button--loading) {
102
+ background: var(--color-primary-60);
103
+ }
104
+ .sds-button--primary:active:not(:disabled):not(.sds-button--loading) {
105
+ background: var(--color-primary-70);
106
+ }
107
+ .sds-button--primary:focus-visible {
108
+ box-shadow: var(--focus-ring);
109
+ }
110
+ /* ──────────────────────────────────────────────
111
+ VARIANT: ghost (outlined / ghost)
112
+ ────────────────────────────────────────────── */
113
+ .sds-button--ghost {
114
+ background-color: transparent;
115
+ border-color: var(--color-primary-50-solid);
116
+ color: var(--color-primary-50-solid);
117
+ }
118
+ .sds-button--ghost:hover:not(:disabled):not(.sds-button--loading) {
119
+ background: var(--color-primary-10);
120
+ border-color: var(--color-primary-60-solid);
121
+ color: var(--color-primary-60-solid);
122
+ }
123
+ .sds-button--ghost:active:not(:disabled):not(.sds-button--loading) {
124
+ background: var(--color-primary-20);
125
+ border-color: var(--color-primary-70-solid);
126
+ color: var(--color-primary-70-solid);
127
+ }
128
+ .sds-button--ghost:focus-visible {
129
+ box-shadow: var(--focus-ring);
130
+ }
131
+ .sds-button--link:focus-visible {
132
+ box-shadow: var(--focus-ring);
133
+ border-radius: var(--radius-sm);
134
+ }
135
+ /* ──────────────────────────────────────────────
136
+ VARIANT: link (text only)
137
+ ────────────────────────────────────────────── */
138
+ .sds-button--link {
139
+ background-color: transparent;
140
+ border: none;
141
+ padding: 0;
142
+ height: 24px;
143
+ font-size: var(--font-size-title-sm);
144
+ line-height: var(--lh-title-sm);
145
+ color: var(--color-primary-50-solid);
146
+ }
147
+ .sds-button--link:hover:not(:disabled):not(.sds-button--loading) {
148
+ color: var(--color-primary-60-solid);
149
+ text-decoration: underline;
150
+ text-underline-offset: 3px;
151
+ }
152
+ .sds-button--link:active:not(:disabled):not(.sds-button--loading) {
153
+ color: var(--color-primary-70-solid);
154
+ }
155
+ /* ──────────────────────────────────────────────
156
+ VARIANT: pillFilled (pill, filled)
157
+ ────────────────────────────────────────────── */
158
+ .sds-button--pillFilled {
159
+ height: 40px;
160
+ padding: 0 var(--spacing-4);
161
+ gap: var(--spacing-2);
162
+ border-radius: var(--radius-full);
163
+ font-size: var(--font-size-body-md);
164
+ font-weight: var(--font-weight-medium);
165
+ line-height: var(--lh-body-md);
166
+ background: var(--color-primary-50);
167
+ color: var(--color-on-primary);
168
+ border: 1px solid var(--color-primary-50-solid);
169
+ }
170
+ .sds-button--pillFilled:hover:not(:disabled):not(.sds-button--loading) {
171
+ background: var(--color-primary-60);
172
+ }
173
+ .sds-button--pillFilled:active:not(:disabled):not(.sds-button--loading) {
174
+ background: var(--color-primary-70);
175
+ }
176
+ .sds-button--pillFilled:focus-visible {
177
+ box-shadow: var(--focus-ring);
178
+ }
179
+ /* ──────────────────────────────────────────────
180
+ VARIANT: pill (pill, outlined)
181
+ ────────────────────────────────────────────── */
182
+ .sds-button--pill {
183
+ height: 40px;
184
+ padding: 0 var(--spacing-4);
185
+ gap: var(--spacing-2);
186
+ border-radius: var(--radius-full);
187
+ font-size: var(--font-size-body-md);
188
+ font-weight: var(--font-weight-medium);
189
+ line-height: var(--lh-body-md);
190
+ background-color: var(--component-bg-color);
191
+ color: var(--color-neutral-80);
192
+ border: 1px solid var(--border-interactive-color);
193
+ }
194
+ .sds-button--pill:hover:not(:disabled):not(.sds-button--loading) {
195
+ border-color: var(--color-primary-50-solid);
196
+ }
197
+ .sds-button--pill:active:not(:disabled):not(.sds-button--loading) {
198
+ background-color: var(--color-neutral-20);
199
+ border-color: var(--color-neutral-60);
200
+ color: var(--color-neutral-90);
201
+ }
202
+ .sds-button--pill:focus-visible {
203
+ box-shadow: var(--focus-ring);
204
+ }
205
+ /* ──────────────────────────────────────────────
206
+ LOADING STATE
207
+ ────────────────────────────────────────────── */
208
+ .sds-button--loading {
209
+ cursor: wait;
210
+ }
211
+ .sds-button--loading .sds-button__label,
212
+ .sds-button--loading .sds-button__icon {
213
+ opacity: 0;
214
+ }
215
+ .sds-button--loading .sds-spinner {
216
+ position: absolute;
217
+ top: 50%;
218
+ left: 50%;
219
+ transform: translate(-50%, -50%);
220
+ }
221
+ /* Primary/pillFilled have a dark background → white loader */
222
+ .sds-button--primary.sds-button--loading .sds-spinner--solid .sds-spinner__arc,
223
+ .sds-button--pillFilled.sds-button--loading .sds-spinner--solid .sds-spinner__arc {
224
+ stroke: var(--color-on-primary);
225
+ }
226
+ .sds-button--primary.sds-button--loading .sds-spinner__track,
227
+ .sds-button--pillFilled.sds-button--loading .sds-spinner__track {
228
+ /* The arc above already reads --color-on-primary, which is white or black by contrast
229
+ against the button's own fill. The track follows it at 30% rather than assuming
230
+ white: on a light primary --color-on-primary is black, and a white track there would
231
+ be invisible. KDLS hardcoded white and had that bug. */
232
+ stroke: color-mix(in srgb, var(--color-on-primary) 30%, transparent);
233
+ }
234
+ /* ──────────────────────────────────────────────
235
+ DISABLED
236
+ ────────────────────────────────────────────── */
237
+ .sds-button:disabled,
238
+ .sds-button[aria-disabled="true"] {
239
+ cursor: not-allowed;
240
+ background-color: var(--color-neutral-20) !important;
241
+ border-color: var(--border-static-color) !important;
242
+ color: var(--color-neutral-40) !important;
243
+ box-shadow: none !important;
244
+ }
245
+ /* Tooltip wraps the button only when disabled: keeps pointer-events:none on the button
246
+ (lets hover reach the wrapper for the tooltip) and shows the cursor on the wrapper */
247
+ .sds-button__tooltip-wrapper {
248
+ cursor: help;
249
+ }
250
+ .sds-button__tooltip-wrapper > .sds-button:disabled,
251
+ .sds-button__tooltip-wrapper > .sds-button[aria-disabled="true"] {
252
+ pointer-events: none;
253
+ }
254
+ .sds-button--link:disabled,
255
+ .sds-button--link[aria-disabled="true"] {
256
+ background-color: transparent !important;
257
+ color: var(--color-neutral-40) !important;
258
+ border-color: transparent !important;
259
+ }
260
+ .sds-button--ghost:disabled,
261
+ .sds-button--ghost[aria-disabled="true"],
262
+ .sds-button--pill:disabled,
263
+ .sds-button--pill[aria-disabled="true"] {
264
+ background-color: transparent !important;
265
+ border-color: var(--border-static-color) !important;
266
+ color: var(--color-neutral-40) !important;
267
+ }
268
+ [data-theme="dark"] .sds-button:disabled,
269
+ [data-theme="dark"] .sds-button[aria-disabled="true"] {
270
+ background-color: var(--color-neutral-30) !important;
271
+ border-color: var(--border-interactive-color) !important;
272
+ color: var(--color-neutral-40) !important;
273
+ }
274
+ [data-theme="dark"] .sds-button--link:disabled,
275
+ [data-theme="dark"] .sds-button--link[aria-disabled="true"] {
276
+ background-color: transparent !important;
277
+ color: var(--color-neutral-40) !important;
278
+ border-color: transparent !important;
279
+ }
280
+ [data-theme="dark"] .sds-button--ghost:disabled,
281
+ [data-theme="dark"] .sds-button--ghost[aria-disabled="true"],
282
+ [data-theme="dark"] .sds-button--pill:disabled,
283
+ [data-theme="dark"] .sds-button--pill[aria-disabled="true"] {
284
+ background-color: transparent !important;
285
+ border-color: var(--border-interactive-color) !important;
286
+ color: var(--color-neutral-40) !important;
287
+ }
288
+ /* ──────────────────────────────────────────────
289
+ NEUTRAL SCHEME DARK MODE OVERRIDE
290
+ In dark mode, neutral-80 resolves to light gray —
291
+ ensure primary button stays legible.
292
+ ────────────────────────────────────────────── */
293
+ [data-theme="dark"] .sds-button--pill:hover:not(:disabled):not(.sds-button--loading) {
294
+ background-color: transparent;
295
+ }
296
+ [data-theme="dark"] .sds-button--pill:active:not(:disabled):not(.sds-button--loading) {
297
+ background-color: var(--color-neutral-30);
298
+ }
299
+ /* Four more dead rules stood here, and they looked at first like a design intention worth
300
+ keeping: they set the primary button's own FILL in the dark theme, to `--color-neutral-80`
301
+ — which reads as "dark" to anyone writing CSS in the light theme.
302
+
303
+ It is not. In the dark theme `--color-neutral-80` resolves to #f2f2f2, exactly what
304
+ `--color-primary-50` resolves to there. Measured side by side in Chrome: both #f2f2f2
305
+ with #262626 text, indistinguishable. The rules asked for the button that already
306
+ exists, spelled with different token names, and they never ran anyway. Nothing to
307
+ preserve. */
308
+
309
+ /* ── Corner radius schemes ────────────────────────────────────────────────────
310
+ A fraction of the button's own height, not `--radius-md`, and for the reason the Pill
311
+ wrote down first: a radius past half the height draws a capsule, and the browser clamps
312
+ anything beyond it. `--radius-md` is 24px at `medium`, which on the 48px button is
313
+ EXACTLY half — so `medium` was already the capsule and `rounded` (32px, clamped back to
314
+ 24) drew the identical shape. Rafael, looking at the two side by side: between medium
315
+ and rounded there is no visual difference, and there has to be.
316
+
317
+ fraction flat 0 soft 0.275 medium 0.375 rounded 0.5 (a capsule)
318
+ 48px 0 13px 18px capsule
319
+ 56px 0 15px 21px capsule
320
+
321
+ Same fractions as Pill (40px → 11 and 15) and as the 40px rows elsewhere, which is what
322
+ keeps every control in the system reading as one family at a given scheme.
323
+
324
+ `pillFilled` and `pill` are not here on purpose: they are capsules by definition —
325
+ being round is what those variants ARE, the way it is for a badge or an avatar — so
326
+ they keep `--radius-full` in every scheme.
327
+
328
+ The bare values above are `soft`'s, because an install that never picks a scheme has no
329
+ attribute at all and falls through to them. */
330
+ [data-radius="flat"] .sds-button--md { border-radius: 0; }
331
+ [data-radius="flat"] .sds-button--lg { border-radius: 0; }
332
+
333
+ [data-radius="soft"] .sds-button--md { border-radius: 13px; }
334
+ [data-radius="soft"] .sds-button--lg { border-radius: 15px; }
335
+
336
+ [data-radius="medium"] .sds-button--md { border-radius: 18px; }
337
+ [data-radius="medium"] .sds-button--lg { border-radius: 21px; }
338
+
339
+ [data-radius="rounded"] .sds-button--md { border-radius: var(--radius-full); }
340
+ [data-radius="rounded"] .sds-button--lg { border-radius: var(--radius-full); }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The Button vocabulary.
3
+ *
4
+ * Ported from kdls-design-system v2.5.1 (0f3edf8) components/Button/Button.tsx.
5
+ * Divergence: none. Every variant already read the primary slot rather than a Kenlo
6
+ * value — what KDLS added on top was a set of [data-color-scheme="lya"] overrides, and
7
+ * those are gone with the scheme. A gradient now reaches the button the same way it
8
+ * reaches everything else: through applyCustomGradient filling the slot.
9
+ */
10
+ export const BUTTON_VARIANTS = ['primary', 'ghost', 'link', 'pill', 'pillFilled'];
11
+ export const BUTTON_SIZES = ['md', 'lg'];
12
+ export const BUTTON_WIDTHS = ['hug', 'fill'];
13
+ export const BUTTON_TYPES = ['button', 'submit', 'reset'];
14
+
15
+ function assertOneOf(label, value, allowed) {
16
+ if (!allowed.includes(value)) {
17
+ throw new Error(`Unknown button ${label}: "${value}". Allowed: ${allowed.join(', ')}`);
18
+ }
19
+ return value;
20
+ }
21
+
22
+ export const assertButtonVariant = (v) => assertOneOf('variant', v, BUTTON_VARIANTS);
23
+ export const assertButtonSize = (v) => assertOneOf('size', v, BUTTON_SIZES);
24
+ export const assertButtonWidth = (v) => assertOneOf('width', v, BUTTON_WIDTHS);
25
+ export const assertButtonType = (v) => assertOneOf('type', v, BUTTON_TYPES);
@@ -0,0 +1,139 @@
1
+ <!-- sds-button — ported from kdls-design-system v2.5.1 (0f3edf8) components/Button/Button.tsx.
2
+
3
+ Hydrates, for the same reason IconButton does: a button exists to be clicked, and Marko 5
4
+ drops a listener silently on a component that does not hydrate. Its input is a label and
5
+ a handful of flags, so the payload is small.
6
+
7
+ Divergences:
8
+
9
+ Sixteen [data-color-scheme="lya"] rules are gone with the scheme they served. They gave
10
+ the primary, ghost, pill and pillFilled variants a gradient treatment; a gradient reaches
11
+ the button through applyCustomGradient filling the primary slot now, and the base rules
12
+ already paint backgrounds from that slot.
13
+
14
+ `disabledTooltip` and `disabledTooltipPosition` are here, and they wrap rather than
15
+ decorate: a disabled <button> dispatches no mouse events and lets none bubble, so a
16
+ tooltip hung on the button itself would never open. KDLS solves it the same way — the
17
+ tooltip's handlers sit on a wrapper and the button inside it is given
18
+ `pointer-events: none`, which is what lets the pointer reach the wrapper at all.
19
+
20
+ Touch is still unanswered for tooltips in general (see sds-tooltip), so on a phone a
21
+ disabled button still explains itself only to a screen reader, through aria-describedby.
22
+
23
+ The spinner track follows --color-on-primary at 30% instead of a hardcoded white. KDLS
24
+ wrote rgba(255,255,255,.30), which is invisible on a light primary — where
25
+ --color-on-primary is black. -->
26
+
27
+ import { assertPlacement } from "../Floating/floating-position.js";
28
+ import { assertButtonVariant, assertButtonSize, assertButtonWidth, assertButtonType }
29
+ from "./button-variants.js";
30
+
31
+ class {
32
+ handleClick(event) {
33
+ const { disabled, isLoading, onClick } = this.input;
34
+ // `disabled` is already enforced by the browser; this guards the loading state,
35
+ // which stays focusable and keeps its shape.
36
+ if (disabled || isLoading) {
37
+ event.preventDefault();
38
+ return;
39
+ }
40
+ onClick?.(event);
41
+ }
42
+ }
43
+
44
+ $ {
45
+ const {
46
+ label,
47
+ variant = "primary",
48
+ size = "md",
49
+ width = "hug",
50
+ type = "button",
51
+ iconLeft,
52
+ iconRight,
53
+ isLoading = false,
54
+ disabled = false,
55
+ disabledTooltip,
56
+ disabledTooltipPosition = "top",
57
+ class: extraClass = "",
58
+ } = input;
59
+
60
+ assertPlacement(disabledTooltipPosition);
61
+
62
+ assertButtonVariant(variant);
63
+ assertButtonSize(size);
64
+ assertButtonWidth(width);
65
+ assertButtonType(type);
66
+
67
+ const ariaLabel = input["aria-label"] ?? input.ariaLabel;
68
+ if (!label && !ariaLabel) {
69
+ throw new Error('sds-button needs a `label`, or an `aria-label` when the button shows only an icon.');
70
+ }
71
+
72
+ const renderable = (value) => (value && value.renderBody) || value;
73
+ const loading = isLoading && !disabled;
74
+
75
+ // The explanation only appears when there is something to explain.
76
+ const explains = Boolean(disabledTooltip) && disabled;
77
+ const wrapperClass = [
78
+ "sds-button__tooltip-wrapper",
79
+ width === "fill" ? "sds-button__tooltip-wrapper--full" : "",
80
+ ].filter(Boolean).join(" ");
81
+
82
+ const classes = [
83
+ "sds-button",
84
+ `sds-button--${variant}`,
85
+ `sds-button--${size}`,
86
+ width === "fill" ? "sds-button--full" : "",
87
+ loading ? "sds-button--loading" : "",
88
+ extraClass,
89
+ ].filter(Boolean).join(" ");
90
+ }
91
+
92
+ <macro|{ }| name="theButton">
93
+ <button
94
+ type=type
95
+ class=classes
96
+ disabled=(disabled || null)
97
+ aria-label=ariaLabel
98
+ aria-disabled=(disabled ? "true" : null)
99
+ aria-busy=(loading ? "true" : null)
100
+ on-click("handleClick")
101
+ >
102
+ <if(loading)>
103
+ <sds-spinner size="sm" decorative/>
104
+ </if>
105
+
106
+ <if(iconLeft)>
107
+ <span class="sds-button__icon sds-button__icon--left" aria-hidden="true">
108
+ <sds-icon>
109
+ <${renderable(iconLeft)}/>
110
+ </sds-icon>
111
+ </span>
112
+ </if>
113
+
114
+ <if(label)>
115
+ <span class="sds-button__label">${label}</span>
116
+ </if>
117
+
118
+ <if(iconRight)>
119
+ <span class="sds-button__icon sds-button__icon--right" aria-hidden="true">
120
+ <sds-icon>
121
+ <${renderable(iconRight)}/>
122
+ </sds-icon>
123
+ </span>
124
+ </if>
125
+ </button>
126
+ </macro>
127
+
128
+ <!-- The tooltip wraps, it does not decorate. A disabled <button> dispatches no mouse
129
+ events and lets none bubble, so handlers on the button itself would never fire; the
130
+ wrapper receives them instead, and Button.css gives the disabled button
131
+ `pointer-events: none` so the pointer reaches the wrapper at all. -->
132
+ <if(explains)>
133
+ <sds-tooltip content=disabledTooltip placement=disabledTooltipPosition class=wrapperClass>
134
+ <theButton/>
135
+ </sds-tooltip>
136
+ </if>
137
+ <else>
138
+ <theButton/>
139
+ </else>