@ethlete/components 1.0.0-next.78 → 1.0.0-next.80

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 (270) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/design/accordion.ts +73 -0
  3. package/design/anchored-dialog.ts +92 -0
  4. package/design/avatar.ts +90 -0
  5. package/design/badge.ts +48 -0
  6. package/design/banner.ts +77 -0
  7. package/design/bracket.ts +457 -0
  8. package/design/breadcrumb.ts +145 -0
  9. package/design/button.ts +62 -1
  10. package/design/calendar.ts +306 -0
  11. package/design/card.ts +92 -0
  12. package/design/carousel.ts +240 -0
  13. package/design/chart.ts +512 -0
  14. package/design/checkbox.ts +275 -0
  15. package/design/chip.ts +64 -0
  16. package/design/config.json +8 -0
  17. package/design/control.ts +19 -0
  18. package/design/copy-button.ts +59 -0
  19. package/design/description-list.ts +41 -0
  20. package/design/dialog.ts +216 -0
  21. package/design/divider.ts +27 -0
  22. package/design/empty-state.ts +45 -0
  23. package/design/floating-action.ts +55 -0
  24. package/design/form-field.ts +3 -0
  25. package/design/grid.ts +148 -0
  26. package/design/input.ts +86 -0
  27. package/design/kbd.ts +19 -0
  28. package/design/loader.ts +152 -0
  29. package/design/masonry.ts +115 -0
  30. package/design/match.ts +131 -0
  31. package/design/menu.ts +147 -0
  32. package/design/notification.ts +133 -0
  33. package/design/otp-input.ts +107 -0
  34. package/design/pagination.ts +120 -0
  35. package/design/picture.ts +80 -0
  36. package/design/progress-steps.ts +79 -0
  37. package/design/query-error.ts +60 -0
  38. package/design/radio-group.ts +132 -0
  39. package/design/scheduler.ts +369 -0
  40. package/design/scrollable.ts +218 -0
  41. package/design/scrollbar.ts +47 -0
  42. package/design/sdk/assets/fallback.svg +1 -0
  43. package/design/sdk/assets/tall.svg +1 -0
  44. package/design/sdk/assets/wide.svg +1 -0
  45. package/design/sdk/components/accent-on-light/accent-on-light.css +35 -0
  46. package/design/sdk/components/accent-on-light/accent-on-light.ts +10 -0
  47. package/design/sdk/components/accent-on-light/shared/controls.ts +46 -0
  48. package/design/sdk/components/accordion/accordion.css +17 -0
  49. package/design/sdk/components/accordion/accordion.ts +68 -0
  50. package/design/sdk/components/anchored-dialog/anchored-dialog.css +63 -0
  51. package/design/sdk/components/anchored-dialog/anchored-dialog.ts +65 -0
  52. package/design/sdk/components/avatar/avatar.css +14 -0
  53. package/design/sdk/components/avatar/avatar.ts +79 -0
  54. package/design/sdk/components/badge/badge.css +13 -0
  55. package/design/sdk/components/badge/badge.ts +53 -0
  56. package/design/sdk/components/banner/banner.css +18 -0
  57. package/design/sdk/components/banner/banner.ts +88 -0
  58. package/design/sdk/components/bracket/bracket.css +59 -0
  59. package/design/sdk/components/bracket/bracket.ts +144 -0
  60. package/design/sdk/components/breadcrumb/breadcrumb.css +25 -0
  61. package/design/sdk/components/breadcrumb/breadcrumb.ts +82 -0
  62. package/design/sdk/components/button/button.css +41 -0
  63. package/design/sdk/components/button/button.ts +59 -7
  64. package/design/sdk/components/calendar/calendar.css +33 -0
  65. package/design/sdk/components/calendar/calendar.ts +97 -0
  66. package/design/sdk/components/card/card.css +19 -0
  67. package/design/sdk/components/card/card.ts +62 -0
  68. package/design/sdk/components/carousel/carousel.css +44 -0
  69. package/design/sdk/components/carousel/carousel.ts +75 -0
  70. package/design/sdk/components/chart/chart.css +56 -0
  71. package/design/sdk/components/chart/chart.ts +99 -0
  72. package/design/sdk/components/checkbox/checkbox.css +65 -0
  73. package/design/sdk/components/checkbox/checkbox.ts +180 -0
  74. package/design/sdk/components/chip/chip.css +18 -0
  75. package/design/sdk/components/chip/chip.ts +77 -0
  76. package/design/sdk/components/copy-button/copy-button.css +14 -0
  77. package/design/sdk/components/copy-button/copy-button.ts +60 -0
  78. package/design/sdk/components/description-list/description-list.css +7 -0
  79. package/design/sdk/components/description-list/description-list.ts +54 -0
  80. package/design/sdk/components/dialog/dialog.css +29 -0
  81. package/design/sdk/components/dialog/dialog.ts +79 -0
  82. package/design/sdk/components/divider/divider.css +26 -0
  83. package/design/sdk/components/divider/divider.ts +44 -0
  84. package/design/sdk/components/empty-state/empty-state.css +15 -0
  85. package/design/sdk/components/empty-state/empty-state.ts +57 -0
  86. package/design/sdk/components/floating-action/floating-action.css +51 -0
  87. package/design/sdk/components/floating-action/floating-action.ts +63 -0
  88. package/design/sdk/components/form-field/form-field.ts +23 -5
  89. package/design/sdk/components/grid/grid.css +17 -0
  90. package/design/sdk/components/grid/grid.ts +81 -0
  91. package/design/sdk/components/input/input.css +75 -0
  92. package/design/sdk/components/input/input.ts +80 -0
  93. package/design/sdk/components/kbd/kbd.css +25 -0
  94. package/design/sdk/components/kbd/kbd.ts +58 -0
  95. package/design/sdk/components/loader/loader.css +37 -0
  96. package/design/sdk/components/loader/loader.ts +69 -0
  97. package/design/sdk/components/masonry/masonry.css +7 -0
  98. package/design/sdk/components/masonry/masonry.ts +43 -0
  99. package/design/sdk/components/match/match.css +25 -0
  100. package/design/sdk/components/match/match.ts +77 -0
  101. package/design/sdk/components/menu/menu.css +22 -0
  102. package/design/sdk/components/menu/menu.ts +103 -0
  103. package/design/sdk/components/notification/notification.css +32 -0
  104. package/design/sdk/components/notification/notification.ts +107 -0
  105. package/design/sdk/components/otp-input/otp-input.css +35 -0
  106. package/design/sdk/components/otp-input/otp-input.ts +65 -0
  107. package/design/sdk/components/pagination/pagination.css +14 -0
  108. package/design/sdk/components/pagination/pagination.ts +76 -0
  109. package/design/sdk/components/picture/picture.css +37 -0
  110. package/design/sdk/components/picture/picture.ts +97 -0
  111. package/design/sdk/components/progress-steps/progress-steps.css +37 -0
  112. package/design/sdk/components/progress-steps/progress-steps.ts +92 -0
  113. package/design/sdk/components/query-error/query-error.css +33 -0
  114. package/design/sdk/components/query-error/query-error.ts +121 -0
  115. package/design/sdk/components/radio-group/radio-group.css +46 -0
  116. package/design/sdk/components/radio-group/radio-group.ts +167 -0
  117. package/design/sdk/components/scheduler/scheduler.css +55 -0
  118. package/design/sdk/components/scheduler/scheduler.ts +240 -0
  119. package/design/sdk/components/scrollable/scrollable.css +58 -0
  120. package/design/sdk/components/scrollable/scrollable.ts +133 -0
  121. package/design/sdk/components/scrollbar/scrollbar.css +78 -0
  122. package/design/sdk/components/scrollbar/scrollbar.ts +124 -0
  123. package/design/sdk/components/select/select.ts +1 -1
  124. package/design/sdk/components/select/shared/select-part.ts +25 -3
  125. package/design/sdk/components/select-panel/select-panel.css +3 -0
  126. package/design/sdk/components/select-panel/select-panel.ts +125 -0
  127. package/design/sdk/components/skeleton/skeleton.css +22 -0
  128. package/design/sdk/components/skeleton/skeleton.ts +51 -0
  129. package/design/sdk/components/slider/slider.css +52 -0
  130. package/design/sdk/components/slider/slider.ts +87 -0
  131. package/design/sdk/components/standings/standings.css +33 -0
  132. package/design/sdk/components/standings/standings.ts +131 -0
  133. package/design/sdk/components/stat-tile/stat-tile.css +15 -0
  134. package/design/sdk/components/stat-tile/stat-tile.ts +85 -0
  135. package/design/sdk/components/stream/stream.css +48 -0
  136. package/design/sdk/components/stream/stream.ts +43 -0
  137. package/design/sdk/components/switch/switch.css +37 -0
  138. package/design/sdk/components/switch/switch.ts +63 -0
  139. package/design/sdk/components/table/table.css +67 -0
  140. package/design/sdk/components/table/table.ts +161 -0
  141. package/design/sdk/components/tabs/tabs.css +13 -0
  142. package/design/sdk/components/tabs/tabs.ts +74 -0
  143. package/design/sdk/components/textarea/textarea.css +116 -0
  144. package/design/sdk/components/textarea/textarea.ts +65 -0
  145. package/design/sdk/components/timeline/timeline.css +30 -0
  146. package/design/sdk/components/timeline/timeline.ts +87 -0
  147. package/design/sdk/components/toolbar/toolbar.css +24 -0
  148. package/design/sdk/components/toolbar/toolbar.ts +88 -0
  149. package/design/sdk/components/tooltip/tooltip.css +15 -0
  150. package/design/sdk/components/tooltip/tooltip.ts +28 -0
  151. package/design/sdk/components/tree/selected-tint.css +15 -0
  152. package/design/sdk/components/tree/tree.ts +70 -0
  153. package/design/sdk/foundations/colors/colors.css +124 -4
  154. package/design/sdk/foundations/colors/colors.ts +1 -1
  155. package/design/sdk/foundations/colors/shared/contrast.ts +1 -1
  156. package/design/sdk/foundations/elevation/elevation.css +6 -0
  157. package/design/sdk/foundations/elevation/elevation.ts +11 -0
  158. package/design/sdk/foundations/elevation/shared/elevation/scene.css +101 -0
  159. package/design/sdk/foundations/elevation/shared/elevation/scene.ts +74 -0
  160. package/design/sdk/foundations/icons/icons.ts +119 -0
  161. package/design/sdk/pictures.json +1183 -0
  162. package/design/select.ts +5 -4
  163. package/design/skeleton.ts +67 -0
  164. package/design/slider.ts +155 -0
  165. package/design/standings.ts +161 -0
  166. package/design/stat-tile.ts +109 -0
  167. package/design/stream.ts +92 -0
  168. package/design/switch.ts +83 -0
  169. package/design/table.ts +302 -0
  170. package/design/tabs.ts +75 -0
  171. package/design/textarea.ts +94 -0
  172. package/design/timeline.ts +52 -0
  173. package/design/toolbar.ts +95 -0
  174. package/design/tooltip.ts +87 -0
  175. package/fesm2022/ethlete-components.mjs +1440 -1209
  176. package/fesm2022/ethlete-components.mjs.map +1 -1
  177. package/generators/generators.json +2 -0
  178. package/package.json +1 -1
  179. package/src/lib/accordion/accordion.component.css +2 -1
  180. package/src/lib/avatar/avatar.component.css +10 -0
  181. package/src/lib/badge/badge.component.css +2 -2
  182. package/src/lib/banner/banner.component.css +3 -1
  183. package/src/lib/breadcrumb/breadcrumb-outlet.component.css +5 -0
  184. package/src/lib/breadcrumb/breadcrumb.component.css +5 -1
  185. package/src/lib/button/button-properties-styles.component.css +6 -6
  186. package/src/lib/button/button.component.css +27 -4
  187. package/src/lib/button/button.types.ts +33 -0
  188. package/src/lib/button/icon-button.component.css +21 -22
  189. package/src/lib/button/split-button.component.css +5 -1
  190. package/src/lib/button/window-control-button.component.css +43 -3
  191. package/src/lib/calendar/calendar.component.css +15 -5
  192. package/src/lib/card/card.component.css +4 -0
  193. package/src/lib/carousel/carousel-autoplay-styles.component.css +1 -1
  194. package/src/lib/carousel/carousel.component.css +2 -2
  195. package/src/lib/chart/chart-axis.component.css +3 -3
  196. package/src/lib/chip/chip.component.css +12 -6
  197. package/src/lib/description-list/description-list.component.css +2 -1
  198. package/src/lib/empty-state/empty-state.component.css +4 -0
  199. package/src/lib/floating-action/floating-action-styles.component.css +27 -0
  200. package/src/lib/focus-ring/focus-ring-styles.component.css +4 -1
  201. package/src/lib/forms/checkbox/checkbox.component.css +31 -5
  202. package/src/lib/forms/color-input/color-input.component.css +3 -1
  203. package/src/lib/forms/color-input/color-picker-panel.component.css +6 -2
  204. package/src/lib/forms/date-time/date-input/date-input.component.css +1 -1
  205. package/src/lib/forms/date-time/date-time-input/date-time-input.component.css +1 -1
  206. package/src/lib/forms/date-time/duration-input/duration-input.component.css +1 -1
  207. package/src/lib/forms/date-time/range-input-shell-styles.component.css +1 -1
  208. package/src/lib/forms/date-time/time-input/time-input.component.css +1 -1
  209. package/src/lib/forms/description/description.component.css +7 -0
  210. package/src/lib/forms/dropzone/dropzone.component.css +26 -0
  211. package/src/lib/forms/form-field/form-field-control-suffix-styles.component.css +4 -2
  212. package/src/lib/forms/form-field/form-field-text-shell-styles.component.css +54 -5
  213. package/src/lib/forms/form-field/form-field-textarea-styles.component.css +62 -0
  214. package/src/lib/forms/form-field/form-field.component.css +6 -0
  215. package/src/lib/forms/form-field/headless/label.directive.css +1 -1
  216. package/src/lib/forms/input/input.component.css +6 -2
  217. package/src/lib/forms/input/number-input.component.css +2 -2
  218. package/src/lib/forms/input/password-input.component.css +4 -3
  219. package/src/lib/forms/otp-input/otp-input.component.css +23 -4
  220. package/src/lib/forms/phone-input/phone-input.component.css +4 -3
  221. package/src/lib/forms/rating/rating.component.css +2 -1
  222. package/src/lib/forms/rich-text-editor/rich-text-editor.component.css +1 -1
  223. package/src/lib/forms/selection-card-styles.component.css +3 -2
  224. package/src/lib/forms/selection-list/checkbox-group/checkbox-group-select-all.component.css +4 -2
  225. package/src/lib/forms/selection-list/checkbox-group/checkbox-option.component.css +4 -2
  226. package/src/lib/forms/selection-list/radio-group/radio.component.css +5 -2
  227. package/src/lib/forms/selection-list/segmented-button-group/segmented-button.component.css +2 -1
  228. package/src/lib/forms/slider/range-slider.component.css +98 -6
  229. package/src/lib/forms/slider/slider.component.css +96 -4
  230. package/src/lib/forms/switch/switch.component.css +45 -15
  231. package/src/lib/forms/tag-input/tag-input.component.css +2 -2
  232. package/src/lib/forms/textarea/textarea-autosize-styles.component.css +2 -3
  233. package/src/lib/forms/textarea/textarea.component.css +7 -2
  234. package/src/lib/grid/grid-item-toolbar.component.css +10 -0
  235. package/src/lib/grid/grid-item.component.css +6 -1
  236. package/src/lib/kbd/kbd.component.css +1 -1
  237. package/src/lib/loader/brand-loader/brand-loader.component.css +4 -1
  238. package/src/lib/loader/progress-bar/progress-bar.component.css +1 -1
  239. package/src/lib/loader/spinner/spinner-styles.component.css +1 -1
  240. package/src/lib/match/match-card.component.css +9 -3
  241. package/src/lib/match/match-score.component.css +4 -1
  242. package/src/lib/menu/menu-selection-item.component.css +1 -1
  243. package/src/lib/menu/menu.component.css +6 -1
  244. package/src/lib/notification/notification.component.css +3 -3
  245. package/src/lib/overlay/overlay-container.component.css +4 -0
  246. package/src/lib/pagination/pagination.component.css +4 -0
  247. package/src/lib/progress-steps/progress-step.component.css +4 -4
  248. package/src/lib/query-error/query-error.component.css +4 -0
  249. package/src/lib/scheduler/scheduler-appointment-styles.component.css +21 -5
  250. package/src/lib/scheduler/scheduler-month-view.component.css +4 -2
  251. package/src/lib/scheduler/scheduler-time-grid-view.component.css +8 -2
  252. package/src/lib/scrollable/headless/scrollable-navigation.component.css +1 -1
  253. package/src/lib/standings/standings-pick.component.css +1 -1
  254. package/src/lib/standings/standings.component.css +10 -10
  255. package/src/lib/stat-tile/stat-tile-sparkline.component.css +1 -1
  256. package/src/lib/stream/consent/stream-consent.component.css +99 -0
  257. package/src/lib/stream/error/stream-player-error.component.css +100 -0
  258. package/src/lib/stream/loading/stream-player-loading.component.css +16 -0
  259. package/src/lib/stream/pip/pip-slot-placeholder.component.css +68 -0
  260. package/src/lib/stream/stream-player-slot-styles.component.css +4 -0
  261. package/src/lib/table/table-resize-grip.component.css +2 -1
  262. package/src/lib/table/table-select-cell.component.css +45 -15
  263. package/src/lib/table/table.component.css +15 -5
  264. package/src/lib/tabs/tab-bar-styles.component.css +1 -1
  265. package/src/lib/tabs/tabs/tab-group.component.css +111 -0
  266. package/src/lib/time-picker/time-picker.component.css +2 -1
  267. package/src/lib/timeline/timeline-item.component.css +5 -1
  268. package/src/lib/toolbar/toolbar.component.css +4 -0
  269. package/src/lib/tooltip/tooltip.component.css +14 -0
  270. package/types/ethlete-components.d.ts +156 -128
@@ -0,0 +1,57 @@
1
+ import { emptyState, emptyStateStyles } from '@ethlete/components/design/empty-state';
2
+ import { definePart, drawing } from '@design-system';
3
+ import frame from './empty-state.css?inline';
4
+
5
+ type Interaction = 'hover' | 'active' | 'focus-visible';
6
+
7
+ type Props = {
8
+ form: 'default' | 'without-action' | 'errored' | 'heading-only';
9
+ action?: Interaction;
10
+ };
11
+
12
+ const mark = (interaction?: Interaction) =>
13
+ [`data-et-control="action" data-can-animate`, interaction && `data-et-force="${interaction}"`]
14
+ .filter(Boolean)
15
+ .join(' ');
16
+
17
+ const COPY = {
18
+ default: {
19
+ heading: 'No results',
20
+ description: 'Try a different search term or clear your filters.',
21
+ action: 'Clear filters',
22
+ },
23
+ 'without-action': { heading: 'No results', description: 'Try a different search term or clear your filters.' },
24
+ errored: {
25
+ icon: 'triangle',
26
+ heading: 'Something went wrong',
27
+ description: 'We could not load this data. Please try again.',
28
+ action: 'Try again',
29
+ },
30
+ 'heading-only': { icon: false, heading: 'Nothing here yet' },
31
+ } as const;
32
+
33
+ const render = ({ form, action }: Props) => emptyState({ ...COPY[form], actionAttributes: mark(action) });
34
+
35
+ export default definePart({
36
+ note: 'Decided rules only',
37
+ madeBy: ['claude-opus-5-5'],
38
+ states: {
39
+ default: { form: 'default' },
40
+ 'without-action': { form: 'without-action' },
41
+ errored: { form: 'errored' },
42
+ 'heading-only': { form: 'heading-only' },
43
+ 'action-hover': { form: 'default', action: 'hover' },
44
+ 'action-active': { form: 'default', action: 'active' },
45
+ 'action-focus-visible': { form: 'default', action: 'focus-visible' },
46
+ },
47
+ widths: [480],
48
+ controls: {
49
+ action: { kind: 'button', input: ['pointer', 'keyboard'] },
50
+ },
51
+ frame: (body) => `<div class="empty-state-frame">${body}</div>`,
52
+ render: (props: Props) =>
53
+ drawing({
54
+ body: render(props),
55
+ styles: [emptyStateStyles, frame].join('\n'),
56
+ }),
57
+ });
@@ -0,0 +1,51 @@
1
+ .fa-frame {
2
+ position: relative;
3
+ overflow: hidden;
4
+ box-sizing: border-box;
5
+ block-size: 400px;
6
+ transform: translate(0);
7
+ background: var(--et-surface-background-solid);
8
+ color: var(--et-surface-color-solid);
9
+ font-size: 1.4rem;
10
+
11
+ .fa-page {
12
+ padding: 2.4rem;
13
+ }
14
+
15
+ .et-floating-action {
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 1.6rem;
19
+ }
20
+
21
+ p {
22
+ margin: 0;
23
+ }
24
+
25
+ .fa-heading {
26
+ margin: 0;
27
+ font-size: 2rem;
28
+ font-weight: 600;
29
+ }
30
+
31
+ .fa-rows {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 0.8rem;
35
+ margin: 0;
36
+ padding: 0;
37
+ }
38
+
39
+ .fa-row {
40
+ padding: 1.2rem;
41
+ border-radius: 8px;
42
+ background: color-mix(in srgb, var(--et-surface-color-solid) 8%, transparent);
43
+ list-style: none;
44
+ }
45
+ }
46
+
47
+ @layer components {
48
+ .et-floating-action .et-floating-action-trigger:where([data-floating-action-state='floating']) {
49
+ --et-button-shadow: inset 0 0 0 1px light-dark(var(--et-theme-color-ink-solid), transparent);
50
+ }
51
+ }
@@ -0,0 +1,63 @@
1
+ import { floatingAction, floatingActionStyles } from '@ethlete/components/design/floating-action';
2
+ import { definePart, drawing } from '@design-system';
3
+ import frame from './floating-action.css?inline';
4
+
5
+ type Interaction = 'hover' | 'active' | 'focus-visible';
6
+
7
+ type Props = {
8
+ state?: 'inline' | 'floating' | 'hidden';
9
+ interaction?: Interaction;
10
+ filterIcon?: boolean;
11
+ color?: string;
12
+ inset?: number;
13
+ overRow?: boolean;
14
+ };
15
+
16
+ const SCROLL = { inline: 0, floating: 236, hidden: 980 };
17
+ const OVER_ROW_SCROLL = 252;
18
+ const PARAGRAPH =
19
+ 'Pick a competition and a season, then narrow the list down by team, venue or kick-off time to find the match you are after.';
20
+
21
+ const rows = Array.from({ length: 12 }, (_, index) => `<li class="fa-row">Result ${index + 1}</li>`).join('');
22
+ const paragraphs = (count: number) => Array.from({ length: count }, () => `<p>${PARAGRAPH}</p>`).join('');
23
+
24
+ const render = ({ state = 'inline', interaction, filterIcon, color, inset }: Props) =>
25
+ floatingAction({
26
+ state,
27
+ filterIcon,
28
+ trigger: { label: 'Filter (2)', color },
29
+ triggerAttributes: interaction ? `data-et-force="${interaction}"` : undefined,
30
+ heldSize: { inlineSize: filterIcon ? 132 : 104, blockSize: 40 },
31
+ before: `${paragraphs(2)}<h2 class="fa-heading">Results</h2>`,
32
+ scope: `<ul class="fa-rows">${rows}</ul>`,
33
+ after: paragraphs(8),
34
+ attributes:
35
+ inset === undefined
36
+ ? undefined
37
+ : `style="--et-floating-action-inset-inline-end: ${inset}px; --et-floating-action-inset-block-end: ${inset}px"`,
38
+ });
39
+
40
+ export default definePart({
41
+ note: 'Q138: the floating trigger gets the Q99a 1px inset ink edge in light only, so it reaches 3:1 on a light grey row (4.68) and the page (5.48); dark unchanged.',
42
+ madeBy: ['claude-opus-5-5'],
43
+ states: {
44
+ inline: {},
45
+ floating: { state: 'floating' },
46
+ 'floating-hover': { state: 'floating', interaction: 'hover' },
47
+ 'floating-active': { state: 'floating', interaction: 'active' },
48
+ 'floating-focus-visible': { state: 'floating', interaction: 'focus-visible' },
49
+ 'inline-focus-visible': { interaction: 'focus-visible' },
50
+ 'floating-icon': { state: 'floating', filterIcon: true },
51
+ 'floating-inset': { state: 'floating', inset: 12 },
52
+ 'floating-danger': { state: 'floating', color: 'danger' },
53
+ 'floating-danger-hover': { state: 'floating', color: 'danger', interaction: 'hover' },
54
+ 'floating-over-row': { state: 'floating', overRow: true },
55
+ hidden: { state: 'hidden' },
56
+ },
57
+ widths: [480],
58
+ render: (props: Props) =>
59
+ drawing({
60
+ body: `<div class="fa-frame" data-et-skip="CLIPPED"><div class="fa-page" style="margin-block-start: -${props.overRow ? OVER_ROW_SCROLL : SCROLL[props.state ?? 'inline']}px">${render(props)}</div></div>`,
61
+ styles: [floatingActionStyles, frame].join('\n'),
62
+ }),
63
+ });
@@ -16,6 +16,7 @@ type Axes = Pick<FormFieldMockOptions, 'appearance' | 'fill' | 'labelMode' | 'si
16
16
  type Props = Axes & {
17
17
  control?: FieldControl;
18
18
  value?: string;
19
+ textAlign?: 'center' | 'end';
19
20
  hint?: string;
20
21
  error?: string;
21
22
  focused?: boolean;
@@ -40,7 +41,7 @@ const axisRows = Object.fromEntries(
40
41
  ),
41
42
  ) as Record<string, Props>;
42
43
 
43
- const { states, grid } = interactionRows(
44
+ const interactive = interactionRows(
44
45
  {
45
46
  empty: { hint: 'Shown on your profile' },
46
47
  filled: { value: 'Ada Lovelace' },
@@ -56,6 +57,23 @@ const { states, grid } = interactionRows(
56
57
  },
57
58
  );
58
59
 
60
+ const ALIGNMENTS = ['center', 'end'] as const;
61
+
62
+ const alignStates = Object.fromEntries(
63
+ ALIGNMENTS.map((textAlign) => [`text-align-${textAlign}`, { ...axisRow, textAlign }]),
64
+ ) as Record<`text-align-${(typeof ALIGNMENTS)[number]}`, Props>;
65
+
66
+ const states = { ...interactive.states, ...alignStates };
67
+ const grid = {
68
+ columns: interactive.grid.columns,
69
+ rows: {
70
+ ...interactive.grid.rows,
71
+ ...Object.fromEntries(
72
+ ALIGNMENTS.map((textAlign) => [`textAlign ${textAlign}`, { rest: `text-align-${textAlign}` as const }]),
73
+ ),
74
+ },
75
+ };
76
+
59
77
  const markControl = (markup: string, control: FieldControl, interaction: FieldInteraction | undefined) => {
60
78
  const frame = ['data-et-control-part', interaction && `data-et-force="${interaction}"`].filter(Boolean);
61
79
  const native = control === 'input' ? '<input class="et-input-native"' : '<textarea class="et-textarea-native"';
@@ -67,7 +85,7 @@ const markControl = (markup: string, control: FieldControl, interaction: FieldIn
67
85
  };
68
86
 
69
87
  export default definePart<Props>({
70
- note: 'The SDK form field with a text input and a textarea, drawn by its mocks from @ethlete/components/design.',
88
+ note: 'scale of the decided shape, Q182, lead merge',
71
89
  madeBy: ['claude-opus-5-5'],
72
90
  states: {
73
91
  ...states,
@@ -82,14 +100,14 @@ export default definePart<Props>({
82
100
  input: { kind: 'field', input: ['pointer', 'keyboard'], disabled: true },
83
101
  textarea: { kind: 'field', input: ['pointer', 'keyboard'], disabled: true },
84
102
  },
85
- render: ({ control = 'input', value, interaction, ...field }) =>
103
+ render: ({ control = 'input', value, textAlign, interaction, ...field }) =>
86
104
  drawing({
87
105
  body: markControl(
88
106
  formField({
89
107
  id: 'name',
90
108
  label: 'Name',
91
- errorColor: 'danger-on-light',
92
- control: control === 'input' ? input({ value }) : textarea({ value }),
109
+ errorColor: 'danger',
110
+ control: control === 'input' ? input({ value, textAlign }) : textarea({ value }),
93
111
  ...field,
94
112
  }),
95
113
  control,
@@ -0,0 +1,17 @@
1
+ .t-frame {
2
+ box-sizing: border-box;
3
+ padding: 1.6rem;
4
+ background: var(--et-surface-background-solid);
5
+ color: var(--et-surface-color-solid);
6
+ font-size: 1.4rem;
7
+ }
8
+
9
+ @layer components {
10
+ .et-grid-item:focus-visible {
11
+ outline-color: var(--et-theme-color-ink-solid);
12
+ }
13
+
14
+ .et-grid-item-toolbar .et-focus-ring {
15
+ --_et-focus-ring-color: var(--et-theme-color-ink-solid);
16
+ }
17
+ }
@@ -0,0 +1,81 @@
1
+ import { grid, GridMockItem, gridStyles } from '@ethlete/components/design/grid';
2
+ import { definePart, drawing } from '@design-system';
3
+ import frame from './grid.css?inline';
4
+
5
+ type Props = {
6
+ layout: 'rest' | 'spans';
7
+ actions?: boolean;
8
+ item?: Partial<GridMockItem> & { id: string };
9
+ drag?: boolean;
10
+ resize?: boolean;
11
+ longTitle?: boolean;
12
+ };
13
+
14
+ const REST: (GridMockItem & { id: string })[] = [
15
+ { id: 'revenue', col: 0, row: 0, colSpan: 2, rowSpan: 1, title: 'Revenue', subtitle: 'Last 30 days' },
16
+ { id: 'visitors', col: 2, row: 0, colSpan: 2, rowSpan: 1, title: 'Visitors', subtitle: 'This week' },
17
+ { id: 'orders', col: 0, row: 1, colSpan: 2, rowSpan: 1, title: 'Orders', subtitle: 'Today' },
18
+ { id: 'refunds', col: 2, row: 1, colSpan: 2, rowSpan: 1, title: 'Refunds', subtitle: 'Today' },
19
+ ];
20
+
21
+ const SPANS: (GridMockItem & { id: string })[] = [
22
+ { id: 'revenue', col: 0, row: 0, colSpan: 2, rowSpan: 1, title: 'Revenue', subtitle: 'Last 30 days' },
23
+ { id: 'visitors', col: 2, row: 0, colSpan: 2, rowSpan: 2, title: 'Visitors', subtitle: 'This week' },
24
+ { id: 'orders', col: 0, row: 1, colSpan: 1, rowSpan: 1, title: 'Orders', subtitle: 'Today' },
25
+ { id: 'refunds', col: 1, row: 1, colSpan: 1, rowSpan: 1, title: 'Refunds', subtitle: 'Today' },
26
+ { id: 'activity', col: 0, row: 2, colSpan: 4, rowSpan: 1, title: 'Activity', subtitle: 'Latest events' },
27
+ ];
28
+
29
+ const LONG_TITLE = 'Revenue by region and sales channel';
30
+
31
+ const itemsOf = (props: Props) => {
32
+ const items = (props.layout === 'rest' ? REST : SPANS).map((item) =>
33
+ props.item?.id === item.id ? { ...item, ...props.item } : item,
34
+ );
35
+
36
+ if (props.drag) {
37
+ return items.map((item) => {
38
+ if (item.id === 'orders') return { ...item, state: 'dragging' as const, offset: { x: 70, y: 18 } };
39
+ if (item.id === 'refunds') return { ...item, col: 0 };
40
+ return item;
41
+ });
42
+ }
43
+
44
+ if (props.resize) {
45
+ return items.map((item) =>
46
+ item.id === 'activity' ? { ...item, rowSpan: 2, state: 'resizing' as const, activeEdge: 's' as const } : item,
47
+ );
48
+ }
49
+
50
+ if (props.longTitle) return items.map((item) => (item.id === 'revenue' ? { ...item, title: LONG_TITLE } : item));
51
+
52
+ return items;
53
+ };
54
+
55
+ export default definePart({
56
+ note: 'Decided rules only',
57
+ madeBy: ['claude-opus-5-5'],
58
+ states: {
59
+ rest: { layout: 'rest' },
60
+ spans: { layout: 'spans' },
61
+ 'item-hover': { layout: 'spans', item: { id: 'visitors', force: 'hover' } },
62
+ 'item-focus-visible': { layout: 'spans', item: { id: 'visitors', force: 'focus-visible' } },
63
+ actions: { layout: 'spans', actions: true },
64
+ 'action-hover': { layout: 'spans', actions: true, item: { id: 'revenue', removeForce: 'hover' } },
65
+ 'action-focus-visible': { layout: 'spans', actions: true, item: { id: 'revenue', removeForce: 'focus-visible' } },
66
+ dragging: { layout: 'spans', drag: true },
67
+ resizing: { layout: 'spans', resize: true },
68
+ 'long-title': { layout: 'spans', actions: true, longTitle: true },
69
+ },
70
+ widths: [480],
71
+ frame: (body) => `<div class="t-frame">${body}</div>`,
72
+ render: (props: Props) =>
73
+ drawing({
74
+ body: grid({
75
+ items: itemsOf(props),
76
+ actions: props.actions,
77
+ ghost: props.drag ? { col: 1, row: 1, colSpan: 1, rowSpan: 1 } : undefined,
78
+ }),
79
+ styles: [gridStyles, frame].join('\n'),
80
+ }),
81
+ });
@@ -0,0 +1,75 @@
1
+ .t-frame {
2
+ box-sizing: border-box;
3
+ display: grid;
4
+ gap: 1.6rem;
5
+ padding: 1.6rem;
6
+ background: var(--et-surface-background-solid);
7
+ color: var(--et-surface-color-solid);
8
+ font-size: 1.4rem;
9
+ }
10
+
11
+ @layer components {
12
+ et-form-field[data-text-shell] {
13
+ --_et-form-field-frame-border: var(--et-surface-interaction-solid);
14
+ --_et-form-field-frame-border-hover: color-mix(
15
+ in srgb,
16
+ var(--et-surface-interaction-solid) 75%,
17
+ var(--et-surface-color-solid)
18
+ );
19
+ --_et-form-field-frame-focus: var(--et-surface-color-solid);
20
+ }
21
+
22
+ et-form-field[data-text-shell][data-error] {
23
+ --_et-form-field-frame-border: var(--et-theme-color-primary-solid);
24
+ --_et-form-field-frame-border-hover: light-dark(
25
+ rgb(var(--et-color-primary-hover, var(--et-color-primary))),
26
+ rgb(
27
+ var(
28
+ --et-color-primary-ink-active,
29
+ var(
30
+ --et-color-primary-ink-focus,
31
+ var(
32
+ --et-color-primary-ink-hover,
33
+ var(
34
+ --et-color-primary-ink,
35
+ var(
36
+ --et-color-primary-active,
37
+ var(--et-color-primary-focus, var(--et-color-primary-hover, var(--et-color-primary)))
38
+ )
39
+ )
40
+ )
41
+ )
42
+ )
43
+ )
44
+ );
45
+ --_et-form-field-frame-focus: var(--et-theme-color-ink-solid);
46
+ }
47
+
48
+ et-form-field[data-text-shell]:is([data-focused], :has(:focus-visible)):not([data-readonly])
49
+ .et-form-field-control-frame {
50
+ border-color: var(--_et-form-field-frame-focus);
51
+ box-shadow: inset 0 0 0 1px var(--_et-form-field-frame-focus);
52
+ }
53
+
54
+ et-form-field[data-text-shell][data-readonly] .et-form-field-control-frame {
55
+ border-style: dashed;
56
+ }
57
+
58
+ et-form-field[data-text-shell][data-readonly]:is([data-focused], :has(:focus-visible)) .et-form-field-control-frame {
59
+ border-color: transparent;
60
+ outline: 2px dashed var(--_et-form-field-frame-focus);
61
+ outline-offset: -2px;
62
+ }
63
+
64
+ et-form-field[data-text-shell] .et-form-field-prefix:not(:has(svg)) {
65
+ margin-inline-end: calc(4px - var(--et-form-field-control-affix-gap));
66
+ }
67
+
68
+ et-form-field et-label .et-label-required-marker {
69
+ color: var(--et-theme-color-ink-solid);
70
+ }
71
+
72
+ et-input .et-input-native::placeholder {
73
+ color: color-mix(in srgb, currentColor 60%, transparent);
74
+ }
75
+ }
@@ -0,0 +1,80 @@
1
+ import { textInput, type TextInputMockOptions, textInputStyles } from '@ethlete/components/design/input';
2
+ import { definePart, drawing } from '@design-system';
3
+ import frame from './input.css?inline';
4
+
5
+ type Props = { field: TextInputMockOptions };
6
+
7
+ const email = (extra: Partial<TextInputMockOptions> = {}): Props => ({
8
+ field: { id: 'email', label: 'Email', placeholder: 'name@example.com', ...extra },
9
+ });
10
+
11
+ const VALUE = 'lena.hoffmann@example.com';
12
+
13
+ const INVALID = {
14
+ required: true,
15
+ value: 'lena.hoffmann@',
16
+ error: 'Enter a valid email address.',
17
+ errorColor: 'danger',
18
+ } satisfies Partial<TextInputMockOptions>;
19
+
20
+ export default definePart({
21
+ note: 'scale of the decided shape, Q182, lead merge',
22
+ madeBy: ['claude-opus-5-5'],
23
+ states: {
24
+ empty: email({ placeholder: undefined }),
25
+ placeholder: email(),
26
+ filled: email({ value: VALUE }),
27
+ hover: email({ force: 'hover' }),
28
+ 'hover-filled': email({ value: VALUE, force: 'hover' }),
29
+ focus: email({ focused: true, force: 'focus-visible' }),
30
+ 'focus-filled': email({ value: VALUE, focused: true, force: 'focus-visible' }),
31
+ 'focus-hover': email({ value: VALUE, focused: true, force: ['focus-visible', 'hover'] }),
32
+ hint: email({ hint: 'We only use it to sign you in.' }),
33
+ description: email({ description: 'Your team sees this address on your profile.' }),
34
+ required: email({ required: true, hint: 'We only use it to sign you in.' }),
35
+ invalid: email(INVALID),
36
+ 'invalid-hover': email({ ...INVALID, force: 'hover' }),
37
+ 'invalid-focus': email({ ...INVALID, focused: true, force: 'focus-visible' }),
38
+ 'invalid-focus-hover': email({ ...INVALID, focused: true, force: ['focus-visible', 'hover'] }),
39
+ disabled: email({ disabled: true }),
40
+ 'disabled-filled': email({ value: VALUE, disabled: true }),
41
+ readonly: email({ value: VALUE, readonly: true }),
42
+ 'readonly-hover': email({ value: VALUE, readonly: true, force: 'hover' }),
43
+ 'readonly-focus': email({ value: VALUE, readonly: true, focused: true, force: 'focus-visible' }),
44
+ long: email({ value: 'lena.hoffmann-schulze.marketing-department@example-company.com' }),
45
+ 'icon-affixes': {
46
+ field: { id: 'key', label: 'API key', placeholder: 'sk-…', prefixIcon: 'lock', suffixIcon: 'check' },
47
+ },
48
+ 'text-affixes': {
49
+ field: { id: 'site', label: 'Website', value: 'example.com', prefix: 'https://', suffix: '.de' },
50
+ },
51
+ 'text-prefix': {
52
+ field: { id: 'price', label: 'Price', value: '1,250.00', prefix: '€' },
53
+ },
54
+ search: {
55
+ field: { id: 'search', label: 'Search', type: 'search', value: 'running shoes' },
56
+ },
57
+ password: {
58
+ field: { id: 'password', label: 'Password', type: 'password', value: 'correct-horse' },
59
+ },
60
+ 'align-end': {
61
+ field: { id: 'amount', label: 'Amount', value: '1,250.00', textAlign: 'end', suffix: 'EUR' },
62
+ },
63
+ mixed: {
64
+ field: { id: 'title', label: 'Title', value: 'shared draft title', mixed: true, mixedLabel: 'Mixed values' },
65
+ },
66
+ 'no-label': {
67
+ field: { id: 'quick', placeholder: 'Search…', prefixIcon: 'lock' },
68
+ },
69
+ 'size-sm': email({ value: VALUE, size: 'sm' }),
70
+ 'size-lg': email({ value: VALUE, size: 'lg' }),
71
+ 'align-center': email({ value: VALUE, textAlign: 'center' }),
72
+ },
73
+ widths: [320, 360, 640],
74
+ frame: (body) => `<div class="t-frame">${body}</div>`,
75
+ render: (props: Props) =>
76
+ drawing({
77
+ body: textInput(props.field),
78
+ styles: [textInputStyles, frame].join('\n'),
79
+ }),
80
+ });
@@ -0,0 +1,25 @@
1
+ .kbd-frame {
2
+ padding: 2.4rem 1.6rem;
3
+ background: var(--et-surface-background-solid);
4
+ color: var(--et-surface-color-solid);
5
+ }
6
+
7
+ .kbd-text {
8
+ margin: 0;
9
+ font-size: 1.4rem;
10
+ line-height: 1.6;
11
+ }
12
+
13
+ .kbd-list {
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: 1.2rem;
17
+ font-size: 1.3rem;
18
+ }
19
+
20
+ .kbd-row {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: space-between;
24
+ gap: 1.6rem;
25
+ }
@@ -0,0 +1,58 @@
1
+ import { kbd, kbdStyles } from '@ethlete/components/design/kbd';
2
+ import { definePart, drawing } from '@design-system';
3
+ import frame from './kbd.css?inline';
4
+
5
+ type Platform = 'apple' | 'other';
6
+
7
+ type Props = {
8
+ form: 'single' | 'combo' | 'in-text' | 'shortcut-list';
9
+ platform: Platform;
10
+ };
11
+
12
+ const MOD = { apple: '⌘', other: 'Ctrl' } as const;
13
+ const SHIFT = { apple: '⇧', other: 'Shift' } as const;
14
+ const ALT = { apple: '⌥', other: 'Alt' } as const;
15
+
16
+ const SHORTCUTS = (p: Platform) => [
17
+ { label: 'Save', keys: [MOD[p], 'S'] },
18
+ { label: 'Undo', keys: [MOD[p], 'Z'] },
19
+ { label: 'Redo', keys: [MOD[p], SHIFT[p], 'Z'] },
20
+ { label: 'Move up a level', keys: [ALT[p], '↑'] },
21
+ { label: 'Next page', keys: ['PgDn'] },
22
+ { label: 'Zoom in', keys: [MOD[p], '+'] },
23
+ ];
24
+
25
+ const render = ({ form, platform: p }: Props) => {
26
+ switch (form) {
27
+ case 'single':
28
+ return kbd({ keys: ['Esc'] });
29
+ case 'combo':
30
+ return kbd({ keys: [MOD[p], 'K'] });
31
+ case 'in-text':
32
+ return `<p class="kbd-text">Press ${kbd({ keys: [MOD[p], 'K'] })} to open the palette, then ${kbd({ keys: ['Esc'] })} to dismiss it.</p>`;
33
+ case 'shortcut-list':
34
+ return `<div class="kbd-list">${SHORTCUTS(p)
35
+ .map(({ label, keys }) => `<div class="kbd-row"><span>${label}</span>${kbd({ keys })}</div>`)
36
+ .join('')}</div>`;
37
+ }
38
+ };
39
+
40
+ export default definePart({
41
+ note: "Today's look",
42
+ madeBy: ['claude-opus-5-5'],
43
+ states: {
44
+ single: { form: 'single', platform: 'apple' },
45
+ 'combo-apple': { form: 'combo', platform: 'apple' },
46
+ 'combo-other': { form: 'combo', platform: 'other' },
47
+ 'in-text': { form: 'in-text', platform: 'apple' },
48
+ 'shortcut-list-apple': { form: 'shortcut-list', platform: 'apple' },
49
+ 'shortcut-list-other': { form: 'shortcut-list', platform: 'other' },
50
+ },
51
+ widths: [480],
52
+ frame: (body) => `<div class="kbd-frame">${body}</div>`,
53
+ render: (props: Props) =>
54
+ drawing({
55
+ body: render(props),
56
+ styles: [kbdStyles, frame].join('\n'),
57
+ }),
58
+ });
@@ -0,0 +1,37 @@
1
+ .loader-frame {
2
+ padding: 2.4rem 1.6rem;
3
+ background: var(--et-surface-background-solid);
4
+ color: var(--et-surface-color-solid);
5
+ }
6
+
7
+ .loader-row {
8
+ display: flex;
9
+ align-items: center;
10
+ gap: 2.4rem;
11
+ }
12
+
13
+ .loader-column {
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: 2rem;
17
+ }
18
+
19
+ @layer components {
20
+ .et-spinner.et-color--brand {
21
+ --et-spinner-color: var(--et-theme-color-ink-solid);
22
+ }
23
+
24
+ .et-progress-bar.et-color--brand {
25
+ --et-progress-bar-indicator-color: var(--et-theme-color-ink-solid);
26
+ }
27
+
28
+ .et-brand-loader .et-brand-loader__outline {
29
+ stroke: var(--et-theme-color-ink-solid);
30
+ }
31
+
32
+ .et-brand-loader .et-brand-loader__fill {
33
+ stroke: var(--et-theme-color-ink-solid);
34
+ stroke-width: 0.7;
35
+ stroke-linejoin: round;
36
+ }
37
+ }