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

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 (252) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/design/accordion.ts +73 -0
  3. package/design/avatar.ts +90 -0
  4. package/design/badge.ts +48 -0
  5. package/design/banner.ts +77 -0
  6. package/design/bracket.ts +457 -0
  7. package/design/breadcrumb.ts +145 -0
  8. package/design/button.ts +49 -0
  9. package/design/calendar.ts +306 -0
  10. package/design/card.ts +92 -0
  11. package/design/carousel.ts +240 -0
  12. package/design/chart.ts +512 -0
  13. package/design/checkbox.ts +275 -0
  14. package/design/chip.ts +64 -0
  15. package/design/config.json +4 -1
  16. package/design/control.ts +19 -0
  17. package/design/copy-button.ts +59 -0
  18. package/design/description-list.ts +41 -0
  19. package/design/dialog.ts +214 -0
  20. package/design/divider.ts +27 -0
  21. package/design/empty-state.ts +45 -0
  22. package/design/floating-action.ts +55 -0
  23. package/design/grid.ts +148 -0
  24. package/design/input.ts +86 -0
  25. package/design/kbd.ts +19 -0
  26. package/design/loader.ts +152 -0
  27. package/design/masonry.ts +115 -0
  28. package/design/match.ts +131 -0
  29. package/design/menu.ts +143 -0
  30. package/design/notification.ts +129 -0
  31. package/design/otp-input.ts +107 -0
  32. package/design/pagination.ts +120 -0
  33. package/design/picture.ts +80 -0
  34. package/design/progress-steps.ts +79 -0
  35. package/design/query-error.ts +60 -0
  36. package/design/radio-group.ts +132 -0
  37. package/design/scheduler.ts +369 -0
  38. package/design/scrollable.ts +218 -0
  39. package/design/scrollbar.ts +47 -0
  40. package/design/sdk/assets/fallback.svg +1 -0
  41. package/design/sdk/assets/tall.svg +1 -0
  42. package/design/sdk/assets/wide.svg +1 -0
  43. package/design/sdk/components/accent-on-light/accent-on-light.css +35 -0
  44. package/design/sdk/components/accent-on-light/accent-on-light.ts +10 -0
  45. package/design/sdk/components/accent-on-light/shared/controls.ts +46 -0
  46. package/design/sdk/components/accordion/accordion.css +17 -0
  47. package/design/sdk/components/accordion/accordion.ts +64 -0
  48. package/design/sdk/components/avatar/avatar.css +14 -0
  49. package/design/sdk/components/avatar/avatar.ts +78 -0
  50. package/design/sdk/components/badge/badge.css +13 -0
  51. package/design/sdk/components/badge/badge.ts +52 -0
  52. package/design/sdk/components/banner/banner.css +18 -0
  53. package/design/sdk/components/banner/banner.ts +87 -0
  54. package/design/sdk/components/bracket/bracket.css +59 -0
  55. package/design/sdk/components/bracket/bracket.ts +143 -0
  56. package/design/sdk/components/breadcrumb/breadcrumb.css +25 -0
  57. package/design/sdk/components/breadcrumb/breadcrumb.ts +81 -0
  58. package/design/sdk/components/button/button.css +33 -0
  59. package/design/sdk/components/button/button.ts +50 -7
  60. package/design/sdk/components/calendar/calendar.css +33 -0
  61. package/design/sdk/components/calendar/calendar.ts +96 -0
  62. package/design/sdk/components/card/card.css +19 -0
  63. package/design/sdk/components/card/card.ts +61 -0
  64. package/design/sdk/components/carousel/carousel.css +44 -0
  65. package/design/sdk/components/carousel/carousel.ts +74 -0
  66. package/design/sdk/components/chart/chart.css +56 -0
  67. package/design/sdk/components/chart/chart.ts +98 -0
  68. package/design/sdk/components/chip/chip.css +18 -0
  69. package/design/sdk/components/chip/chip.ts +76 -0
  70. package/design/sdk/components/copy-button/copy-button.css +14 -0
  71. package/design/sdk/components/copy-button/copy-button.ts +59 -0
  72. package/design/sdk/components/description-list/description-list.css +7 -0
  73. package/design/sdk/components/description-list/description-list.ts +53 -0
  74. package/design/sdk/components/dialog/dialog.css +29 -0
  75. package/design/sdk/components/dialog/dialog.ts +78 -0
  76. package/design/sdk/components/divider/divider.css +26 -0
  77. package/design/sdk/components/divider/divider.ts +43 -0
  78. package/design/sdk/components/empty-state/empty-state.css +15 -0
  79. package/design/sdk/components/empty-state/empty-state.ts +56 -0
  80. package/design/sdk/components/floating-action/floating-action.css +51 -0
  81. package/design/sdk/components/floating-action/floating-action.ts +63 -0
  82. package/design/sdk/components/form-field/form-field.ts +23 -5
  83. package/design/sdk/components/grid/grid.css +17 -0
  84. package/design/sdk/components/grid/grid.ts +80 -0
  85. package/design/sdk/components/input/input.css +75 -0
  86. package/design/sdk/components/input/input.ts +79 -0
  87. package/design/sdk/components/kbd/kbd.css +25 -0
  88. package/design/sdk/components/kbd/kbd.ts +57 -0
  89. package/design/sdk/components/loader/loader.css +37 -0
  90. package/design/sdk/components/loader/loader.ts +68 -0
  91. package/design/sdk/components/masonry/masonry.css +7 -0
  92. package/design/sdk/components/masonry/masonry.ts +42 -0
  93. package/design/sdk/components/match/match.css +25 -0
  94. package/design/sdk/components/match/match.ts +76 -0
  95. package/design/sdk/components/menu/menu.css +22 -0
  96. package/design/sdk/components/menu/menu.ts +91 -0
  97. package/design/sdk/components/notification/notification.css +32 -0
  98. package/design/sdk/components/notification/notification.ts +106 -0
  99. package/design/sdk/components/otp-input/otp-input.css +35 -0
  100. package/design/sdk/components/otp-input/otp-input.ts +64 -0
  101. package/design/sdk/components/pagination/pagination.css +14 -0
  102. package/design/sdk/components/pagination/pagination.ts +75 -0
  103. package/design/sdk/components/picture/picture.css +37 -0
  104. package/design/sdk/components/picture/picture.ts +96 -0
  105. package/design/sdk/components/progress-steps/progress-steps.css +37 -0
  106. package/design/sdk/components/progress-steps/progress-steps.ts +91 -0
  107. package/design/sdk/components/query-error/query-error.css +33 -0
  108. package/design/sdk/components/query-error/query-error.ts +120 -0
  109. package/design/sdk/components/scheduler/scheduler.css +55 -0
  110. package/design/sdk/components/scheduler/scheduler.ts +239 -0
  111. package/design/sdk/components/scrollable/scrollable.css +58 -0
  112. package/design/sdk/components/scrollable/scrollable.ts +133 -0
  113. package/design/sdk/components/scrollbar/scrollbar.css +78 -0
  114. package/design/sdk/components/scrollbar/scrollbar.ts +123 -0
  115. package/design/sdk/components/select/select.ts +1 -1
  116. package/design/sdk/components/select/shared/select-part.ts +16 -1
  117. package/design/sdk/components/select-panel/select-panel.css +3 -0
  118. package/design/sdk/components/select-panel/select-panel.ts +124 -0
  119. package/design/sdk/components/skeleton/skeleton.css +22 -0
  120. package/design/sdk/components/skeleton/skeleton.ts +50 -0
  121. package/design/sdk/components/slider/slider.css +52 -0
  122. package/design/sdk/components/slider/slider.ts +86 -0
  123. package/design/sdk/components/standings/standings.css +33 -0
  124. package/design/sdk/components/standings/standings.ts +130 -0
  125. package/design/sdk/components/stat-tile/stat-tile.css +15 -0
  126. package/design/sdk/components/stat-tile/stat-tile.ts +84 -0
  127. package/design/sdk/components/stream/stream.css +48 -0
  128. package/design/sdk/components/stream/stream.ts +38 -0
  129. package/design/sdk/components/switch/switch.css +35 -0
  130. package/design/sdk/components/switch/switch.ts +63 -0
  131. package/design/sdk/components/table/table.css +67 -0
  132. package/design/sdk/components/table/table.ts +160 -0
  133. package/design/sdk/components/tabs/tabs.css +13 -0
  134. package/design/sdk/components/tabs/tabs.ts +74 -0
  135. package/design/sdk/components/textarea/textarea.css +116 -0
  136. package/design/sdk/components/textarea/textarea.ts +64 -0
  137. package/design/sdk/components/timeline/timeline.css +30 -0
  138. package/design/sdk/components/timeline/timeline.ts +87 -0
  139. package/design/sdk/components/toolbar/toolbar.css +24 -0
  140. package/design/sdk/components/toolbar/toolbar.ts +87 -0
  141. package/design/sdk/components/tooltip/tooltip.css +15 -0
  142. package/design/sdk/components/tooltip/tooltip.ts +28 -0
  143. package/design/sdk/components/tree/selected-tint.css +15 -0
  144. package/design/sdk/components/tree/tree.ts +70 -0
  145. package/design/sdk/foundations/colors/colors.css +108 -4
  146. package/design/sdk/foundations/elevation/elevation.css +6 -0
  147. package/design/sdk/foundations/elevation/elevation.ts +11 -0
  148. package/design/sdk/foundations/elevation/shared/elevation/scene.css +101 -0
  149. package/design/sdk/foundations/elevation/shared/elevation/scene.ts +74 -0
  150. package/design/sdk/foundations/icons/icons.ts +119 -0
  151. package/design/skeleton.ts +67 -0
  152. package/design/slider.ts +155 -0
  153. package/design/standings.ts +161 -0
  154. package/design/stat-tile.ts +109 -0
  155. package/design/stream.ts +92 -0
  156. package/design/switch.ts +83 -0
  157. package/design/table.ts +302 -0
  158. package/design/tabs.ts +75 -0
  159. package/design/textarea.ts +92 -0
  160. package/design/timeline.ts +52 -0
  161. package/design/toolbar.ts +95 -0
  162. package/design/tooltip.ts +87 -0
  163. package/fesm2022/ethlete-components.mjs +228 -187
  164. package/fesm2022/ethlete-components.mjs.map +1 -1
  165. package/package.json +1 -1
  166. package/src/lib/accordion/accordion.component.css +2 -1
  167. package/src/lib/avatar/avatar.component.css +10 -0
  168. package/src/lib/badge/badge.component.css +2 -2
  169. package/src/lib/banner/banner.component.css +3 -1
  170. package/src/lib/breadcrumb/breadcrumb-outlet.component.css +5 -0
  171. package/src/lib/breadcrumb/breadcrumb.component.css +5 -1
  172. package/src/lib/button/button.component.css +19 -0
  173. package/src/lib/button/icon-button.component.css +19 -20
  174. package/src/lib/button/split-button.component.css +5 -1
  175. package/src/lib/calendar/calendar.component.css +13 -3
  176. package/src/lib/card/card.component.css +4 -0
  177. package/src/lib/carousel/carousel-autoplay-styles.component.css +1 -1
  178. package/src/lib/carousel/carousel.component.css +2 -2
  179. package/src/lib/chart/chart-axis.component.css +3 -3
  180. package/src/lib/chip/chip.component.css +8 -2
  181. package/src/lib/description-list/description-list.component.css +2 -1
  182. package/src/lib/empty-state/empty-state.component.css +4 -0
  183. package/src/lib/floating-action/floating-action-styles.component.css +27 -0
  184. package/src/lib/focus-ring/focus-ring-styles.component.css +4 -1
  185. package/src/lib/forms/checkbox/checkbox.component.css +31 -5
  186. package/src/lib/forms/color-input/color-input.component.css +3 -1
  187. package/src/lib/forms/color-input/color-picker-panel.component.css +6 -2
  188. package/src/lib/forms/date-time/date-input/date-input.component.css +1 -1
  189. package/src/lib/forms/date-time/date-time-input/date-time-input.component.css +1 -1
  190. package/src/lib/forms/date-time/duration-input/duration-input.component.css +1 -1
  191. package/src/lib/forms/date-time/range-input-shell-styles.component.css +1 -1
  192. package/src/lib/forms/date-time/time-input/time-input.component.css +1 -1
  193. package/src/lib/forms/description/description.component.css +7 -0
  194. package/src/lib/forms/dropzone/dropzone.component.css +26 -0
  195. package/src/lib/forms/form-field/form-field-control-suffix-styles.component.css +4 -2
  196. package/src/lib/forms/form-field/form-field-text-shell-styles.component.css +54 -5
  197. package/src/lib/forms/form-field/form-field-textarea-styles.component.css +62 -0
  198. package/src/lib/forms/form-field/form-field.component.css +6 -0
  199. package/src/lib/forms/form-field/headless/label.directive.css +1 -1
  200. package/src/lib/forms/input/input.component.css +6 -2
  201. package/src/lib/forms/input/number-input.component.css +2 -2
  202. package/src/lib/forms/input/password-input.component.css +4 -3
  203. package/src/lib/forms/otp-input/otp-input.component.css +23 -4
  204. package/src/lib/forms/phone-input/phone-input.component.css +4 -3
  205. package/src/lib/forms/rating/rating.component.css +2 -1
  206. package/src/lib/forms/rich-text-editor/rich-text-editor.component.css +1 -1
  207. package/src/lib/forms/selection-card-styles.component.css +3 -2
  208. package/src/lib/forms/selection-list/checkbox-group/checkbox-group-select-all.component.css +4 -2
  209. package/src/lib/forms/selection-list/checkbox-group/checkbox-option.component.css +4 -2
  210. package/src/lib/forms/selection-list/radio-group/radio.component.css +5 -2
  211. package/src/lib/forms/selection-list/segmented-button-group/segmented-button.component.css +2 -1
  212. package/src/lib/forms/slider/range-slider.component.css +98 -6
  213. package/src/lib/forms/slider/slider.component.css +96 -4
  214. package/src/lib/forms/switch/switch.component.css +47 -4
  215. package/src/lib/forms/tag-input/tag-input.component.css +2 -2
  216. package/src/lib/forms/textarea/textarea-autosize-styles.component.css +2 -3
  217. package/src/lib/forms/textarea/textarea.component.css +7 -2
  218. package/src/lib/grid/grid-item-toolbar.component.css +10 -0
  219. package/src/lib/grid/grid-item.component.css +6 -1
  220. package/src/lib/loader/brand-loader/brand-loader.component.css +4 -1
  221. package/src/lib/loader/progress-bar/progress-bar.component.css +1 -1
  222. package/src/lib/loader/spinner/spinner-styles.component.css +1 -1
  223. package/src/lib/match/match-card.component.css +9 -3
  224. package/src/lib/match/match-score.component.css +4 -1
  225. package/src/lib/menu/menu-selection-item.component.css +1 -1
  226. package/src/lib/menu/menu.component.css +5 -0
  227. package/src/lib/overlay/overlay-container.component.css +4 -0
  228. package/src/lib/pagination/pagination.component.css +4 -0
  229. package/src/lib/progress-steps/progress-step.component.css +4 -4
  230. package/src/lib/query-error/query-error.component.css +4 -0
  231. package/src/lib/scheduler/scheduler-appointment-styles.component.css +21 -5
  232. package/src/lib/scheduler/scheduler-month-view.component.css +4 -2
  233. package/src/lib/scheduler/scheduler-time-grid-view.component.css +8 -2
  234. package/src/lib/scrollable/headless/scrollable-navigation.component.css +1 -1
  235. package/src/lib/standings/standings-pick.component.css +1 -1
  236. package/src/lib/standings/standings.component.css +10 -10
  237. package/src/lib/stat-tile/stat-tile-sparkline.component.css +1 -1
  238. package/src/lib/stream/consent/stream-consent.component.css +99 -0
  239. package/src/lib/stream/error/stream-player-error.component.css +100 -0
  240. package/src/lib/stream/loading/stream-player-loading.component.css +16 -0
  241. package/src/lib/stream/pip/pip-slot-placeholder.component.css +68 -0
  242. package/src/lib/stream/stream-player-slot-styles.component.css +4 -0
  243. package/src/lib/table/table-resize-grip.component.css +2 -1
  244. package/src/lib/table/table-select-cell.component.css +45 -15
  245. package/src/lib/table/table.component.css +15 -5
  246. package/src/lib/tabs/tab-bar-styles.component.css +1 -1
  247. package/src/lib/tabs/tabs/tab-group.component.css +111 -0
  248. package/src/lib/time-picker/time-picker.component.css +2 -1
  249. package/src/lib/timeline/timeline-item.component.css +5 -1
  250. package/src/lib/toolbar/toolbar.component.css +4 -0
  251. package/src/lib/tooltip/tooltip.component.css +14 -0
  252. package/types/ethlete-components.d.ts +11 -2
@@ -0,0 +1,61 @@
1
+ import { button } from '@ethlete/components/design/button';
2
+ import { card, cardContent, CardMockOptions, cardPlaceholderMedia, cardStyles } from '@ethlete/components/design/card';
3
+ import { definePart, drawing } from '@design-system';
4
+ import frame from './card.css?inline';
5
+
6
+ type Interaction = 'hover' | 'active' | 'focus-visible';
7
+
8
+ type Props = {
9
+ variant: NonNullable<CardMockOptions['variant']>;
10
+ form?: 'full' | 'media' | 'plain';
11
+ confirm?: Interaction;
12
+ cancel?: Interaction;
13
+ };
14
+
15
+ const mark = (control: string, interaction?: Interaction) =>
16
+ [`data-et-control="${control}" data-can-animate`, interaction && `data-et-force="${interaction}"`]
17
+ .filter(Boolean)
18
+ .join(' ');
19
+
20
+ const actions = ({ confirm, cancel }: Props) =>
21
+ `<div class="et-card-mock-actions">${[
22
+ button({ label: 'Cancel', variant: 'outline' }).replace(/^<button /, `<button ${mark('cancel', cancel)} `),
23
+ button({ label: 'Open', variant: 'filled' }).replace(/^<button /, `<button ${mark('confirm', confirm)} `),
24
+ ].join('')}</div>`;
25
+
26
+ const content = (props: Props) => {
27
+ if (props.form === 'plain') return cardContent({ title: 'Revenue', text: '$12,400 this month' });
28
+
29
+ return (
30
+ cardContent({
31
+ media: props.form === 'media' ? cardPlaceholderMedia : undefined,
32
+ title: 'Spring league',
33
+ subtitle: 'Starts 12 April',
34
+ text: 'Eight teams play two rounds. Sign-ups close one week before kick-off.',
35
+ }) + actions(props)
36
+ );
37
+ };
38
+
39
+ export default definePart({
40
+ note: 'Decided rules only',
41
+ madeBy: ['claude-opus-5-5'],
42
+ states: {
43
+ outlined: { variant: 'outlined' },
44
+ elevated: { variant: 'elevated' },
45
+ filled: { variant: 'filled' },
46
+ media: { variant: 'outlined', form: 'media' },
47
+ plain: { variant: 'outlined', form: 'plain' },
48
+ 'confirm-focus-visible': { variant: 'outlined', confirm: 'focus-visible' },
49
+ 'cancel-focus-visible': { variant: 'outlined', cancel: 'focus-visible' },
50
+ },
51
+ widths: [480],
52
+ controls: {
53
+ cancel: { kind: 'button', input: ['pointer', 'keyboard'] },
54
+ confirm: { kind: 'button', input: ['pointer', 'keyboard'] },
55
+ },
56
+ render: (props: Props) =>
57
+ drawing({
58
+ body: `<div class="card-frame">${card({ variant: props.variant, content: content(props) })}</div>`,
59
+ styles: [cardStyles, frame].join('\n'),
60
+ }),
61
+ });
@@ -0,0 +1,44 @@
1
+ .carousel-frame {
2
+ padding: 2.4rem;
3
+ background: var(--et-surface-background-solid);
4
+ color: var(--et-surface-color-solid);
5
+ font-size: 1.4rem;
6
+
7
+ .carousel-slide {
8
+ display: flex;
9
+ flex-direction: column;
10
+ justify-content: flex-end;
11
+ gap: 0.4rem;
12
+ box-sizing: border-box;
13
+ block-size: 100%;
14
+ min-block-size: 200px;
15
+ padding: 2.4rem;
16
+ border-radius: 12px;
17
+ background: color-mix(in srgb, var(--et-surface-color-solid) 14%, transparent);
18
+ color: var(--et-surface-color-solid);
19
+ }
20
+
21
+ .carousel-slide p {
22
+ margin: 0;
23
+ }
24
+
25
+ .carousel-slide-title {
26
+ font-size: 2rem;
27
+ font-weight: 600;
28
+ }
29
+
30
+ .carousel-slide-meta {
31
+ font-size: 1.2rem;
32
+ }
33
+ }
34
+
35
+ @layer components {
36
+ .et-carousel .et-focus-ring {
37
+ --_et-focus-ring-color: var(--et-theme-color-ink-solid);
38
+ }
39
+
40
+ .et-carousel .et-carousel-dot[data-active]::before,
41
+ .et-carousel .et-carousel-dot-progress-half::before {
42
+ background: light-dark(var(--et-theme-color-ink-solid), var(--et-theme-color-primary-solid));
43
+ }
44
+ }
@@ -0,0 +1,74 @@
1
+ import { carousel, carouselStyles, type CarouselMockOptions } from '@ethlete/components/design/carousel';
2
+ import { definePart, drawing } from '@design-system';
3
+ import frame from './carousel.css?inline';
4
+
5
+ type Interaction = 'hover' | 'active' | 'focus-visible';
6
+
7
+ type Props = Omit<CarouselMockOptions, 'slides'> & {
8
+ count?: number;
9
+ longCaption?: boolean;
10
+ previous?: Interaction;
11
+ next?: Interaction;
12
+ playToggle?: Interaction;
13
+ dot?: { index: number; interaction: Interaction };
14
+ };
15
+
16
+ const TITLES = ['Kick-off', 'First half', 'Half time', 'Second half', 'Full time'];
17
+ const LONG_CAPTION =
18
+ 'A long caption that wraps over several lines inside the slide, the way a match report teaser runs on past a single line of text.';
19
+
20
+ const force = (interaction: Interaction | undefined) => (interaction ? `data-et-force="${interaction}"` : undefined);
21
+
22
+ const slideContent = (index: number, count: number, longCaption: boolean) =>
23
+ `<div class="carousel-slide"><p class="carousel-slide-title">${index + 1}. ${TITLES[index % TITLES.length]}</p>` +
24
+ `<p>${longCaption && index === 0 ? LONG_CAPTION : 'A short line about the slide.'}</p>` +
25
+ `<p class="carousel-slide-meta">slide ${index + 1} of ${count}</p></div>`;
26
+
27
+ const render = ({ count = 5, longCaption = false, previous, next, playToggle, dot, ...options }: Props) =>
28
+ carousel({
29
+ ...options,
30
+ slides: Array.from({ length: count }, (_, index) => ({ content: slideContent(index, count, longCaption) })),
31
+ previousAttributes: force(previous),
32
+ nextAttributes: force(next),
33
+ playToggleAttributes: force(playToggle),
34
+ dotAttributes: dot ? { [dot.index]: `data-et-force="${dot.interaction}"` } : undefined,
35
+ trackAttributes: 'data-et-skip="CLIPPED"',
36
+ }).replaceAll(
37
+ 'class="et-carousel-dot-progress-half"',
38
+ 'data-et-skip="CLIPPED" class="et-carousel-dot-progress-half"',
39
+ );
40
+
41
+ export default definePart({
42
+ note: 'scale of the decided shape, Q182, lead merge',
43
+ madeBy: ['claude-opus-5-5'],
44
+ states: {
45
+ 'first-slide': {},
46
+ 'middle-slide': { active: 2 },
47
+ 'last-slide': { active: 4 },
48
+ 'no-loop-first': { loop: false },
49
+ 'no-loop-last': { loop: false, active: 4 },
50
+ 'next-hover': { active: 2, next: 'hover' },
51
+ 'next-active': { active: 2, next: 'active' },
52
+ 'next-focus-visible': { active: 2, next: 'focus-visible' },
53
+ 'dot-hover': { active: 2, dot: { index: 3, interaction: 'hover' } },
54
+ 'active-dot-hover': { active: 2, dot: { index: 2, interaction: 'hover' } },
55
+ 'dot-focus-visible': { active: 2, dot: { index: 3, interaction: 'focus-visible' } },
56
+ 'active-dot-focus-visible': { active: 2, dot: { index: 2, interaction: 'focus-visible' } },
57
+ 'autoplay-playing': { active: 1, autoplay: true },
58
+ 'autoplay-paused': { active: 1, autoplay: true, playing: false },
59
+ 'play-toggle-hover': { active: 1, autoplay: true, playToggle: 'hover' },
60
+ 'play-toggle-focus-visible': { active: 1, autoplay: true, playToggle: 'focus-visible' },
61
+ 'multiple-items': { itemSize: 'half', slideGap: 16, active: 1 },
62
+ 'item-size-third': { itemSize: 'third', slideGap: 16, active: 1 },
63
+ 'center-aligned': { itemSize: 'half', slideGap: 16, slideAlign: 'center', active: 2 },
64
+ 'no-controls': { active: 1, showControls: false },
65
+ 'long-caption': { longCaption: true },
66
+ 'single-slide': { count: 1 },
67
+ },
68
+ widths: [480],
69
+ render: (props: Props) =>
70
+ drawing({
71
+ body: `<div class="carousel-frame">${render(props)}</div>`,
72
+ styles: [carouselStyles, frame].join('\n'),
73
+ }),
74
+ });
@@ -0,0 +1,56 @@
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-line-chart-series,
11
+ .et-line-chart-slice .et-line-chart-slice-point,
12
+ .et-chart-legend[data-mark='line'] .et-chart-legend-swatch,
13
+ .et-chart-tooltip .et-chart-tooltip-key,
14
+ .et-line-chart-tooltip .et-line-chart-tooltip-key {
15
+ --_et-chart-mark-accent: color-mix(
16
+ in oklab,
17
+ light-dark(var(--et-theme-color-ink-solid), var(--et-theme-color-primary-solid)) var(--_et-chart-accent-mix, 100%),
18
+ var(--et-surface-background-solid, transparent)
19
+ );
20
+ }
21
+
22
+ .et-line-chart-series {
23
+ --_et-line-chart-color: var(--_et-chart-mark-accent);
24
+ }
25
+
26
+ .et-line-chart-slice .et-line-chart-slice-point {
27
+ fill: var(--_et-chart-mark-accent);
28
+ }
29
+
30
+ .et-chart-legend[data-mark='line'] .et-chart-legend-swatch,
31
+ .et-chart-tooltip .et-chart-tooltip-key,
32
+ .et-line-chart-tooltip .et-line-chart-tooltip-key {
33
+ background: var(--_et-chart-mark-accent);
34
+ }
35
+
36
+ .et-bar-chart-bar .et-bar-chart-bar-mark,
37
+ .et-pie-chart-slice .et-pie-chart-slice-mark {
38
+ stroke: var(--et-theme-color-ink-solid);
39
+ stroke-width: 1px;
40
+ }
41
+
42
+ .et-chart-legend:not([data-mark='line']) .et-chart-legend-swatch,
43
+ .et-pie-chart-legend .et-pie-chart-legend-swatch {
44
+ box-shadow: inset 0 0 0 1px var(--et-theme-color-ink-solid);
45
+ }
46
+
47
+ .et-bar-chart-bar:focus-visible .et-bar-chart-bar-target,
48
+ .et-line-chart-slice:focus-visible .et-line-chart-focus-ring {
49
+ stroke: var(--et-theme-color-ink-solid);
50
+ }
51
+
52
+ .et-pie-chart-slice:focus-visible .et-pie-chart-slice-mark {
53
+ stroke: var(--et-theme-color-ink-solid);
54
+ stroke-width: 2px;
55
+ }
56
+ }
@@ -0,0 +1,98 @@
1
+ import { barChart, chartStyles, ChartMockMark, lineChart, pieChart } from '@ethlete/components/design/chart';
2
+ import { definePart, drawing } from '@design-system';
3
+ import frame from './chart.css?inline';
4
+
5
+ type Kind = 'bar' | 'line' | 'pie';
6
+
7
+ type Props = {
8
+ kind: Kind;
9
+ series?: boolean;
10
+ mark?: ChartMockMark;
11
+ empty?: boolean;
12
+ longLabel?: boolean;
13
+ points?: boolean;
14
+ donut?: boolean;
15
+ };
16
+
17
+ const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
18
+ const LONG_MONTHS = ['January', 'February', 'March', 'April', 'May', 'June'];
19
+ const DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
20
+ const BAR_2026 = { label: '2026', values: [32, 48, 41, 67, 54, 72] };
21
+ const BAR_2025 = { label: '2025', values: [28, 35, 44, 51, 47, 58] };
22
+ const VISITS = { label: 'Visits', values: [42, 55, 49, 63, 71, 38, 30] };
23
+ const SIGNUPS = { label: 'Sign-ups', values: [18, 24, 21, 30, 34, 15, 12] };
24
+ const SOURCES = [
25
+ { label: 'Direct', value: 420 },
26
+ { label: 'Search', value: 280 },
27
+ { label: 'Social', value: 180 },
28
+ { label: 'Referral', value: 120 },
29
+ ];
30
+ const LONG_SOURCES = [
31
+ { label: 'Direct', value: 420 },
32
+ { label: 'Search engines and paid search campaigns', value: 280 },
33
+ { label: 'Social', value: 180 },
34
+ { label: 'Referral', value: 120 },
35
+ ];
36
+
37
+ const PLOT_WIDTH = 410;
38
+
39
+ const render = (props: Props) => {
40
+ if (props.kind === 'bar') {
41
+ return barChart({
42
+ plotWidth: PLOT_WIDTH,
43
+ categories: props.longLabel ? LONG_MONTHS.map((month) => `${month} 2026`) : MONTHS,
44
+ series: props.empty ? [{ label: '2026', values: [] }] : props.series ? [BAR_2025, BAR_2026] : [BAR_2026],
45
+ empty: 'No sales in this period',
46
+ mark: props.mark,
47
+ });
48
+ }
49
+
50
+ if (props.kind === 'line') {
51
+ return lineChart({
52
+ plotWidth: PLOT_WIDTH,
53
+ categories: DAYS,
54
+ series: props.series ? [VISITS, SIGNUPS] : [VISITS],
55
+ points: props.points,
56
+ mark: props.mark,
57
+ });
58
+ }
59
+
60
+ return pieChart({
61
+ data: props.empty ? SOURCES.map((source) => ({ ...source, value: 0 })) : props.longLabel ? LONG_SOURCES : SOURCES,
62
+ innerRadius: props.donut ? 0.6 : 0,
63
+ mark: props.mark,
64
+ });
65
+ };
66
+
67
+ export default definePart({
68
+ note: "The SDK charts (et-bar-chart, et-line-chart, et-pie-chart), first drawing, drawn by their mocks from @ethlete/components/design/chart with the real markup and CSS: bar, line and pie chart sheets plus the axis, grid, legend, chart tooltip and line chart tooltip sheets, and the et-tooltip pane for the open tooltip. Framed as a 480px page with 16px padding; bar and line plots 410px wide, 240px high (the default); pie 200px (the default), donut innerRadius 0.6 with its total. Single series draw in the accent; two or more in accent mix steps (100 / 40 %, pie 100 / 80 / 60 / 40 %). States: bar (Jan to Jun), bar-series (2025 and 2026, legend), bar-hover and bar-focus-visible (Apr), bar-tooltip (Mar 2025, hover with its tooltip), bar-empty (projected etBarChartEmpty text), bar-long-label (January 2026 ... June 2026: the auto label spacing thins to every second label); line, line-series (Visits and Sign-ups, legend), line-points, line-hover and line-focus-visible (Thu: crosshair, slice dots, focus box), line-tooltip (Wed, active slice with the multi-series tooltip); pie, donut, pie-hover and pie-focus-visible (Search), pie-tooltip (Direct), pie-empty (all zero: the empty ring), pie-long-label (a long legend label, ellipsed). Static snapshot: the bar grow, line reveal and slice fade are drawn at rest. The lib has no loading state. Numbers (light / dark), from shot pixels at scale 2, brand mint, a: mint marks (bars, line 1, dots, legend and tooltip keys) 1.33 / 13.50; 40 % series (bar 2026, line 2) 1.13 / 2.79; axis and legend text (muted) 7.81 / 6.94; crosshair (subtle) 4.95 / 4.99; hover and focus tint 1.07 / 1.08; focus rings: bar and line (2px, series colour / primary) 1.33 / 13.50, pie (2px, text colour) 17.93 / 17.18. Q98 numbers only: grid lines vs page 1.26 / 1.73 (as the table's edges). Decided rules that arise: Q88 (bar, line and pie focus rings), Q99a (on light the line strokes, slice dots, line legend swatches and tooltip keys take ink in their mix steps; bars, pie slices and the square legend swatches are mint fills and get a 1px ink edge). Series colours otherwise stay data colours: the mix steps are kept. Q101 does not arise: no filled button. An ink edge arises, so Q102 arises: under the brand theme the dark ink is the mint, so the edge is invisible on the full mint fill but shows as a bright outline around the 40 % series in dark (b), while c drops it there. The thinned first and last category labels sit inside the plot (lib fix 3c26c2204). Findings for later, not render refusals: the pie focus ring in b is the same ink as its new 1px edge, only 1px wider, so a focused slice reads close to rest (a used the text colour); d draws it as an outer ring instead. The pie tooltip opens over the legend. Later rounds, left out here: horizontal and stacked bars, negative values, area and stacked area, the time scale, sankey, per-series colour themes, the title and note slots, touch tap.",
69
+ madeBy: ['claude-opus-5-5'],
70
+ states: {
71
+ bar: { kind: 'bar' },
72
+ 'bar-series': { kind: 'bar', series: true },
73
+ 'bar-hover': { kind: 'bar', mark: { index: 3, force: 'hover' } },
74
+ 'bar-focus-visible': { kind: 'bar', mark: { index: 3, force: 'focus-visible' } },
75
+ 'bar-tooltip': { kind: 'bar', series: true, mark: { index: 4, force: 'hover', tooltip: true } },
76
+ 'bar-empty': { kind: 'bar', empty: true },
77
+ 'bar-long-label': { kind: 'bar', longLabel: true },
78
+ line: { kind: 'line' },
79
+ 'line-series': { kind: 'line', series: true },
80
+ 'line-points': { kind: 'line', points: true },
81
+ 'line-hover': { kind: 'line', series: true, mark: { index: 3, force: 'hover' } },
82
+ 'line-focus-visible': { kind: 'line', series: true, mark: { index: 3, force: 'focus-visible' } },
83
+ 'line-tooltip': { kind: 'line', series: true, mark: { index: 2, force: 'hover', tooltip: true } },
84
+ pie: { kind: 'pie' },
85
+ donut: { kind: 'pie', donut: true },
86
+ 'pie-hover': { kind: 'pie', mark: { index: 1, force: 'hover' } },
87
+ 'pie-focus-visible': { kind: 'pie', mark: { index: 1, force: 'focus-visible' } },
88
+ 'pie-tooltip': { kind: 'pie', mark: { index: 0, force: 'hover', tooltip: true } },
89
+ 'pie-empty': { kind: 'pie', empty: true },
90
+ 'pie-long-label': { kind: 'pie', longLabel: true },
91
+ },
92
+ widths: [480],
93
+ render: (props: Props) =>
94
+ drawing({
95
+ body: `<div class="t-frame">${render(props)}</div>`,
96
+ styles: [chartStyles, frame].join('\n'),
97
+ }),
98
+ });
@@ -0,0 +1,18 @@
1
+ .chip-frame {
2
+ display: flex;
3
+ align-items: center;
4
+ padding: 2.4rem 1.6rem;
5
+ background: var(--et-surface-background-solid);
6
+ color: var(--et-surface-color-solid);
7
+ }
8
+
9
+ @layer components {
10
+ .et-chip:where([role='checkbox'], [role='radio']):focus-visible,
11
+ .et-chip .et-chip-remove-button:focus-visible {
12
+ outline-color: var(--et-theme-color-ink-solid);
13
+ }
14
+
15
+ .et-chip:where([aria-checked='true']) {
16
+ box-shadow: inset 0 0 0 1px var(--et-theme-color-ink-solid);
17
+ }
18
+ }
@@ -0,0 +1,76 @@
1
+ import { chip, chipRow, chipStyles, ChipMockOptions } from '@ethlete/components/design/chip';
2
+ import { definePart, drawing, interactionRows } from '@design-system';
3
+ import frame from './chip.css?inline';
4
+
5
+ type Interaction = 'hover' | 'active' | 'focus-visible';
6
+
7
+ type Props = ChipMockOptions & { interaction?: Interaction; row?: true };
8
+
9
+ const mark = (control: string, interaction?: Interaction) =>
10
+ [`data-et-control="${control}" data-can-animate`, interaction && `data-et-force="${interaction}"`]
11
+ .filter(Boolean)
12
+ .join(' ');
13
+
14
+ const draw = ({ interaction, ...options }: Props) =>
15
+ chip({
16
+ ...options,
17
+ attributes: options.selection ? mark('chip', options.removable ? undefined : interaction) : undefined,
18
+ removeAttributes: options.removable ? mark('remove', interaction) : undefined,
19
+ });
20
+
21
+ const ROW: ChipMockOptions[] = [
22
+ { label: 'Bundesliga' },
23
+ { label: 'U19', removable: true },
24
+ { label: 'Upcoming', selection: 'checkbox' },
25
+ { label: 'Live', selection: 'checkbox', selected: true },
26
+ { label: 'Finished', selection: 'checkbox', selected: true, readonly: true },
27
+ { label: 'Archived', selection: 'checkbox', disabled: true },
28
+ ];
29
+
30
+ export const { states: rows, grid } = interactionRows<
31
+ Props,
32
+ 'removable' | 'filter' | 'selected' | 'danger' | 'disabled' | 'disabled-selected' | 'disabled-removable',
33
+ Interaction
34
+ >(
35
+ {
36
+ removable: { label: 'U19', removable: true },
37
+ filter: { label: 'Upcoming', selection: 'checkbox' },
38
+ selected: { label: 'Live', selection: 'checkbox', selected: true },
39
+ danger: { label: 'Overdue', selection: 'checkbox', selected: true, color: 'danger' },
40
+ disabled: { label: 'Archived', selection: 'checkbox', disabled: true },
41
+ 'disabled-selected': { label: 'Archived', selection: 'checkbox', selected: true, disabled: true },
42
+ 'disabled-removable': { label: 'U19', removable: true, disabled: true },
43
+ },
44
+ { interactions: ['hover', 'active', 'focus-visible'], force: (props, interaction) => ({ ...props, interaction }) },
45
+ );
46
+
47
+ export default definePart({
48
+ note: 'Decided rules only',
49
+ madeBy: ['claude-opus-5-5'],
50
+ states: {
51
+ ...rows,
52
+ plain: { label: 'Bundesliga' },
53
+ readonly: { label: 'Finished', selection: 'checkbox', selected: true, readonly: true },
54
+ row: { label: '', row: true },
55
+ },
56
+ grid,
57
+ widths: [480],
58
+ contrast: {
59
+ exceptions: [
60
+ {
61
+ selector: '.et-chip.et-color--danger .et-chip-label',
62
+ reason:
63
+ "Hovered, the selected danger chip's 24% fill takes its ink label to 4.42:1 on light; the hover step is open in another round (Q101), not this round's question.",
64
+ },
65
+ ],
66
+ },
67
+ controls: {
68
+ chip: { kind: 'button', input: ['pointer', 'keyboard'], disabled: true },
69
+ remove: { kind: 'button', input: ['pointer', 'keyboard'], disabled: true },
70
+ },
71
+ render: (props: Props) =>
72
+ drawing({
73
+ body: `<div class="chip-frame">${props.row ? chipRow(ROW.map((each) => draw(each))) : draw(props)}</div>`,
74
+ styles: [chipStyles, frame].join('\n'),
75
+ }),
76
+ });
@@ -0,0 +1,14 @@
1
+ .copy-button-frame {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: 1.6rem;
5
+ padding: 2.4rem 1.6rem;
6
+ background: var(--et-surface-background-solid);
7
+ color: var(--et-surface-color-solid);
8
+ }
9
+
10
+ @layer components {
11
+ .copy-button-frame .et-focus-ring {
12
+ --_et-focus-ring-color: var(--et-theme-color-ink-solid);
13
+ }
14
+ }
@@ -0,0 +1,59 @@
1
+ import { copyButton, copyButtonStyles } from '@ethlete/components/design/copy-button';
2
+ import { definePart, drawing } from '@design-system';
3
+ import frame from './copy-button.css?inline';
4
+
5
+ type Interaction = 'hover' | 'active' | 'focus-visible';
6
+
7
+ type Props = {
8
+ outcome?: 'copied' | 'failed';
9
+ interaction?: Interaction;
10
+ disabled?: boolean;
11
+ size?: 'xs' | 'sm' | 'lg' | 'xl';
12
+ };
13
+
14
+ const mark = (control: 'icon' | 'text', interaction?: Interaction) =>
15
+ [`data-et-control="${control}" data-can-animate`, interaction && `data-et-force="${interaction}"`]
16
+ .filter(Boolean)
17
+ .join(' ');
18
+
19
+ const render = ({ outcome, interaction, disabled, size }: Props) =>
20
+ ['icon', 'text']
21
+ .map((kind) =>
22
+ copyButton({
23
+ kind: kind as 'icon' | 'text',
24
+ outcome,
25
+ disabled,
26
+ size,
27
+ attributes: mark(kind as 'icon' | 'text', interaction),
28
+ }),
29
+ )
30
+ .join('');
31
+
32
+ export default definePart({
33
+ note: 'scale of the decided shape, Q182, lead merge',
34
+ madeBy: ['claude-opus-5-5'],
35
+ states: {
36
+ idle: {},
37
+ copied: { outcome: 'copied' },
38
+ failed: { outcome: 'failed' },
39
+ hover: { interaction: 'hover' },
40
+ active: { interaction: 'active' },
41
+ 'focus-visible': { interaction: 'focus-visible' },
42
+ 'copied-focus-visible': { outcome: 'copied', interaction: 'focus-visible' },
43
+ disabled: { disabled: true },
44
+ 'size-xs': { size: 'xs' },
45
+ 'size-sm': { size: 'sm' },
46
+ 'size-lg': { size: 'lg' },
47
+ 'size-xl': { size: 'xl' },
48
+ },
49
+ widths: [480],
50
+ controls: {
51
+ icon: { kind: 'button', input: ['pointer', 'keyboard'] },
52
+ text: { kind: 'button', input: ['pointer', 'keyboard'] },
53
+ },
54
+ render: (props: Props) =>
55
+ drawing({
56
+ body: `<div class="copy-button-frame">${render(props)}</div>`,
57
+ styles: [copyButtonStyles, frame].join('\n'),
58
+ }),
59
+ });
@@ -0,0 +1,7 @@
1
+ .description-list-frame {
2
+ max-inline-size: 42rem;
3
+ padding: 3.2rem;
4
+ background: var(--et-surface-background-solid);
5
+ color: var(--et-surface-color-solid);
6
+ font-family: var(--et-font-sans, sans-serif);
7
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ descriptionList,
3
+ descriptionListStyles,
4
+ type DescriptionListMockItem,
5
+ } from '@ethlete/components/design/description-list';
6
+ import { definePart, drawing } from '@design-system';
7
+ import frame from './description-list.css?inline';
8
+
9
+ type Props = {
10
+ variant?: 'inline' | 'stacked';
11
+ items?: DescriptionListMockItem[];
12
+ };
13
+
14
+ const STORY: DescriptionListMockItem[] = [
15
+ { term: 'Name', detail: 'Jane Doe' },
16
+ { term: 'Email', detail: 'jane@example.com' },
17
+ { term: 'Role', detail: 'Administrator' },
18
+ { term: 'Notes', detail: 'Joined during the private beta and has opted into every early-access feature flag.' },
19
+ ];
20
+
21
+ const LONG: DescriptionListMockItem[] = [
22
+ { term: 'Name', detail: 'Jane Doe' },
23
+ { term: 'Profile URL', detail: 'https://example.com/organisations/ethlete/members/jane-doe-administrator-0042' },
24
+ { term: 'Billing contact for invoices', detail: 'accounts-payable@example.com' },
25
+ ];
26
+
27
+ const EMPTY: DescriptionListMockItem[] = [
28
+ { term: 'Name', detail: 'Jane Doe' },
29
+ { term: 'Phone', detail: '' },
30
+ { term: 'Role', detail: 'Administrator' },
31
+ ];
32
+
33
+ const GROUPED: DescriptionListMockItem[] = STORY.map((item) => ({ ...item, grouped: true }));
34
+
35
+ export default definePart({
36
+ note: "Today's look",
37
+ madeBy: ['claude-opus-5-5'],
38
+ states: {
39
+ default: {},
40
+ stacked: { variant: 'stacked' },
41
+ 'long-value': { items: LONG },
42
+ 'long-value-stacked': { items: LONG, variant: 'stacked' },
43
+ 'empty-value': { items: EMPTY },
44
+ grouped: { items: GROUPED },
45
+ 'grouped-stacked': { items: GROUPED, variant: 'stacked' },
46
+ },
47
+ widths: [480],
48
+ render: ({ variant, items = STORY }: Props) =>
49
+ drawing({
50
+ body: `<div class="description-list-frame">${descriptionList({ items, variant })}</div>`,
51
+ styles: [descriptionListStyles, frame].join('\n'),
52
+ }),
53
+ });
@@ -0,0 +1,29 @@
1
+ .dialog-frame {
2
+ overflow: hidden;
3
+ background: var(--et-surface-background-solid);
4
+ color: var(--et-surface-color-solid);
5
+ font-size: 1.4rem;
6
+ }
7
+
8
+ .dialog-frame .et-overlay--dialog {
9
+ inline-size: 100%;
10
+ max-inline-size: 51.2rem;
11
+ }
12
+
13
+ .dialog-frame .et-overlay--dialog:has(.et-overlay-body--dynamic-dividers) {
14
+ max-block-size: 36rem;
15
+ }
16
+
17
+ .dialog-frame .et-overlay-body p {
18
+ margin: 0 0 1.2rem;
19
+ }
20
+
21
+ @layer components {
22
+ .et-overlay--dialog .et-focus-ring {
23
+ --_et-focus-ring-color: var(--et-theme-color-ink-solid);
24
+ }
25
+
26
+ .et-overlay--dialog .et-button[data-variant='filled'] {
27
+ --et-button-shadow: inset 0 0 0 1px var(--et-theme-color-ink-solid);
28
+ }
29
+ }