@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.
- package/CHANGELOG.md +139 -0
- package/design/accordion.ts +73 -0
- package/design/anchored-dialog.ts +92 -0
- package/design/avatar.ts +90 -0
- package/design/badge.ts +48 -0
- package/design/banner.ts +77 -0
- package/design/bracket.ts +457 -0
- package/design/breadcrumb.ts +145 -0
- package/design/button.ts +62 -1
- package/design/calendar.ts +306 -0
- package/design/card.ts +92 -0
- package/design/carousel.ts +240 -0
- package/design/chart.ts +512 -0
- package/design/checkbox.ts +275 -0
- package/design/chip.ts +64 -0
- package/design/config.json +8 -0
- package/design/control.ts +19 -0
- package/design/copy-button.ts +59 -0
- package/design/description-list.ts +41 -0
- package/design/dialog.ts +216 -0
- package/design/divider.ts +27 -0
- package/design/empty-state.ts +45 -0
- package/design/floating-action.ts +55 -0
- package/design/form-field.ts +3 -0
- package/design/grid.ts +148 -0
- package/design/input.ts +86 -0
- package/design/kbd.ts +19 -0
- package/design/loader.ts +152 -0
- package/design/masonry.ts +115 -0
- package/design/match.ts +131 -0
- package/design/menu.ts +147 -0
- package/design/notification.ts +133 -0
- package/design/otp-input.ts +107 -0
- package/design/pagination.ts +120 -0
- package/design/picture.ts +80 -0
- package/design/progress-steps.ts +79 -0
- package/design/query-error.ts +60 -0
- package/design/radio-group.ts +132 -0
- package/design/scheduler.ts +369 -0
- package/design/scrollable.ts +218 -0
- package/design/scrollbar.ts +47 -0
- package/design/sdk/assets/fallback.svg +1 -0
- package/design/sdk/assets/tall.svg +1 -0
- package/design/sdk/assets/wide.svg +1 -0
- package/design/sdk/components/accent-on-light/accent-on-light.css +35 -0
- package/design/sdk/components/accent-on-light/accent-on-light.ts +10 -0
- package/design/sdk/components/accent-on-light/shared/controls.ts +46 -0
- package/design/sdk/components/accordion/accordion.css +17 -0
- package/design/sdk/components/accordion/accordion.ts +68 -0
- package/design/sdk/components/anchored-dialog/anchored-dialog.css +63 -0
- package/design/sdk/components/anchored-dialog/anchored-dialog.ts +65 -0
- package/design/sdk/components/avatar/avatar.css +14 -0
- package/design/sdk/components/avatar/avatar.ts +79 -0
- package/design/sdk/components/badge/badge.css +13 -0
- package/design/sdk/components/badge/badge.ts +53 -0
- package/design/sdk/components/banner/banner.css +18 -0
- package/design/sdk/components/banner/banner.ts +88 -0
- package/design/sdk/components/bracket/bracket.css +59 -0
- package/design/sdk/components/bracket/bracket.ts +144 -0
- package/design/sdk/components/breadcrumb/breadcrumb.css +25 -0
- package/design/sdk/components/breadcrumb/breadcrumb.ts +82 -0
- package/design/sdk/components/button/button.css +41 -0
- package/design/sdk/components/button/button.ts +59 -7
- package/design/sdk/components/calendar/calendar.css +33 -0
- package/design/sdk/components/calendar/calendar.ts +97 -0
- package/design/sdk/components/card/card.css +19 -0
- package/design/sdk/components/card/card.ts +62 -0
- package/design/sdk/components/carousel/carousel.css +44 -0
- package/design/sdk/components/carousel/carousel.ts +75 -0
- package/design/sdk/components/chart/chart.css +56 -0
- package/design/sdk/components/chart/chart.ts +99 -0
- package/design/sdk/components/checkbox/checkbox.css +65 -0
- package/design/sdk/components/checkbox/checkbox.ts +180 -0
- package/design/sdk/components/chip/chip.css +18 -0
- package/design/sdk/components/chip/chip.ts +77 -0
- package/design/sdk/components/copy-button/copy-button.css +14 -0
- package/design/sdk/components/copy-button/copy-button.ts +60 -0
- package/design/sdk/components/description-list/description-list.css +7 -0
- package/design/sdk/components/description-list/description-list.ts +54 -0
- package/design/sdk/components/dialog/dialog.css +29 -0
- package/design/sdk/components/dialog/dialog.ts +79 -0
- package/design/sdk/components/divider/divider.css +26 -0
- package/design/sdk/components/divider/divider.ts +44 -0
- package/design/sdk/components/empty-state/empty-state.css +15 -0
- package/design/sdk/components/empty-state/empty-state.ts +57 -0
- package/design/sdk/components/floating-action/floating-action.css +51 -0
- package/design/sdk/components/floating-action/floating-action.ts +63 -0
- package/design/sdk/components/form-field/form-field.ts +23 -5
- package/design/sdk/components/grid/grid.css +17 -0
- package/design/sdk/components/grid/grid.ts +81 -0
- package/design/sdk/components/input/input.css +75 -0
- package/design/sdk/components/input/input.ts +80 -0
- package/design/sdk/components/kbd/kbd.css +25 -0
- package/design/sdk/components/kbd/kbd.ts +58 -0
- package/design/sdk/components/loader/loader.css +37 -0
- package/design/sdk/components/loader/loader.ts +69 -0
- package/design/sdk/components/masonry/masonry.css +7 -0
- package/design/sdk/components/masonry/masonry.ts +43 -0
- package/design/sdk/components/match/match.css +25 -0
- package/design/sdk/components/match/match.ts +77 -0
- package/design/sdk/components/menu/menu.css +22 -0
- package/design/sdk/components/menu/menu.ts +103 -0
- package/design/sdk/components/notification/notification.css +32 -0
- package/design/sdk/components/notification/notification.ts +107 -0
- package/design/sdk/components/otp-input/otp-input.css +35 -0
- package/design/sdk/components/otp-input/otp-input.ts +65 -0
- package/design/sdk/components/pagination/pagination.css +14 -0
- package/design/sdk/components/pagination/pagination.ts +76 -0
- package/design/sdk/components/picture/picture.css +37 -0
- package/design/sdk/components/picture/picture.ts +97 -0
- package/design/sdk/components/progress-steps/progress-steps.css +37 -0
- package/design/sdk/components/progress-steps/progress-steps.ts +92 -0
- package/design/sdk/components/query-error/query-error.css +33 -0
- package/design/sdk/components/query-error/query-error.ts +121 -0
- package/design/sdk/components/radio-group/radio-group.css +46 -0
- package/design/sdk/components/radio-group/radio-group.ts +167 -0
- package/design/sdk/components/scheduler/scheduler.css +55 -0
- package/design/sdk/components/scheduler/scheduler.ts +240 -0
- package/design/sdk/components/scrollable/scrollable.css +58 -0
- package/design/sdk/components/scrollable/scrollable.ts +133 -0
- package/design/sdk/components/scrollbar/scrollbar.css +78 -0
- package/design/sdk/components/scrollbar/scrollbar.ts +124 -0
- package/design/sdk/components/select/select.ts +1 -1
- package/design/sdk/components/select/shared/select-part.ts +25 -3
- package/design/sdk/components/select-panel/select-panel.css +3 -0
- package/design/sdk/components/select-panel/select-panel.ts +125 -0
- package/design/sdk/components/skeleton/skeleton.css +22 -0
- package/design/sdk/components/skeleton/skeleton.ts +51 -0
- package/design/sdk/components/slider/slider.css +52 -0
- package/design/sdk/components/slider/slider.ts +87 -0
- package/design/sdk/components/standings/standings.css +33 -0
- package/design/sdk/components/standings/standings.ts +131 -0
- package/design/sdk/components/stat-tile/stat-tile.css +15 -0
- package/design/sdk/components/stat-tile/stat-tile.ts +85 -0
- package/design/sdk/components/stream/stream.css +48 -0
- package/design/sdk/components/stream/stream.ts +43 -0
- package/design/sdk/components/switch/switch.css +37 -0
- package/design/sdk/components/switch/switch.ts +63 -0
- package/design/sdk/components/table/table.css +67 -0
- package/design/sdk/components/table/table.ts +161 -0
- package/design/sdk/components/tabs/tabs.css +13 -0
- package/design/sdk/components/tabs/tabs.ts +74 -0
- package/design/sdk/components/textarea/textarea.css +116 -0
- package/design/sdk/components/textarea/textarea.ts +65 -0
- package/design/sdk/components/timeline/timeline.css +30 -0
- package/design/sdk/components/timeline/timeline.ts +87 -0
- package/design/sdk/components/toolbar/toolbar.css +24 -0
- package/design/sdk/components/toolbar/toolbar.ts +88 -0
- package/design/sdk/components/tooltip/tooltip.css +15 -0
- package/design/sdk/components/tooltip/tooltip.ts +28 -0
- package/design/sdk/components/tree/selected-tint.css +15 -0
- package/design/sdk/components/tree/tree.ts +70 -0
- package/design/sdk/foundations/colors/colors.css +124 -4
- package/design/sdk/foundations/colors/colors.ts +1 -1
- package/design/sdk/foundations/colors/shared/contrast.ts +1 -1
- package/design/sdk/foundations/elevation/elevation.css +6 -0
- package/design/sdk/foundations/elevation/elevation.ts +11 -0
- package/design/sdk/foundations/elevation/shared/elevation/scene.css +101 -0
- package/design/sdk/foundations/elevation/shared/elevation/scene.ts +74 -0
- package/design/sdk/foundations/icons/icons.ts +119 -0
- package/design/sdk/pictures.json +1183 -0
- package/design/select.ts +5 -4
- package/design/skeleton.ts +67 -0
- package/design/slider.ts +155 -0
- package/design/standings.ts +161 -0
- package/design/stat-tile.ts +109 -0
- package/design/stream.ts +92 -0
- package/design/switch.ts +83 -0
- package/design/table.ts +302 -0
- package/design/tabs.ts +75 -0
- package/design/textarea.ts +94 -0
- package/design/timeline.ts +52 -0
- package/design/toolbar.ts +95 -0
- package/design/tooltip.ts +87 -0
- package/fesm2022/ethlete-components.mjs +1440 -1209
- package/fesm2022/ethlete-components.mjs.map +1 -1
- package/generators/generators.json +2 -0
- package/package.json +1 -1
- package/src/lib/accordion/accordion.component.css +2 -1
- package/src/lib/avatar/avatar.component.css +10 -0
- package/src/lib/badge/badge.component.css +2 -2
- package/src/lib/banner/banner.component.css +3 -1
- package/src/lib/breadcrumb/breadcrumb-outlet.component.css +5 -0
- package/src/lib/breadcrumb/breadcrumb.component.css +5 -1
- package/src/lib/button/button-properties-styles.component.css +6 -6
- package/src/lib/button/button.component.css +27 -4
- package/src/lib/button/button.types.ts +33 -0
- package/src/lib/button/icon-button.component.css +21 -22
- package/src/lib/button/split-button.component.css +5 -1
- package/src/lib/button/window-control-button.component.css +43 -3
- package/src/lib/calendar/calendar.component.css +15 -5
- package/src/lib/card/card.component.css +4 -0
- package/src/lib/carousel/carousel-autoplay-styles.component.css +1 -1
- package/src/lib/carousel/carousel.component.css +2 -2
- package/src/lib/chart/chart-axis.component.css +3 -3
- package/src/lib/chip/chip.component.css +12 -6
- package/src/lib/description-list/description-list.component.css +2 -1
- package/src/lib/empty-state/empty-state.component.css +4 -0
- package/src/lib/floating-action/floating-action-styles.component.css +27 -0
- package/src/lib/focus-ring/focus-ring-styles.component.css +4 -1
- package/src/lib/forms/checkbox/checkbox.component.css +31 -5
- package/src/lib/forms/color-input/color-input.component.css +3 -1
- package/src/lib/forms/color-input/color-picker-panel.component.css +6 -2
- package/src/lib/forms/date-time/date-input/date-input.component.css +1 -1
- package/src/lib/forms/date-time/date-time-input/date-time-input.component.css +1 -1
- package/src/lib/forms/date-time/duration-input/duration-input.component.css +1 -1
- package/src/lib/forms/date-time/range-input-shell-styles.component.css +1 -1
- package/src/lib/forms/date-time/time-input/time-input.component.css +1 -1
- package/src/lib/forms/description/description.component.css +7 -0
- package/src/lib/forms/dropzone/dropzone.component.css +26 -0
- package/src/lib/forms/form-field/form-field-control-suffix-styles.component.css +4 -2
- package/src/lib/forms/form-field/form-field-text-shell-styles.component.css +54 -5
- package/src/lib/forms/form-field/form-field-textarea-styles.component.css +62 -0
- package/src/lib/forms/form-field/form-field.component.css +6 -0
- package/src/lib/forms/form-field/headless/label.directive.css +1 -1
- package/src/lib/forms/input/input.component.css +6 -2
- package/src/lib/forms/input/number-input.component.css +2 -2
- package/src/lib/forms/input/password-input.component.css +4 -3
- package/src/lib/forms/otp-input/otp-input.component.css +23 -4
- package/src/lib/forms/phone-input/phone-input.component.css +4 -3
- package/src/lib/forms/rating/rating.component.css +2 -1
- package/src/lib/forms/rich-text-editor/rich-text-editor.component.css +1 -1
- package/src/lib/forms/selection-card-styles.component.css +3 -2
- package/src/lib/forms/selection-list/checkbox-group/checkbox-group-select-all.component.css +4 -2
- package/src/lib/forms/selection-list/checkbox-group/checkbox-option.component.css +4 -2
- package/src/lib/forms/selection-list/radio-group/radio.component.css +5 -2
- package/src/lib/forms/selection-list/segmented-button-group/segmented-button.component.css +2 -1
- package/src/lib/forms/slider/range-slider.component.css +98 -6
- package/src/lib/forms/slider/slider.component.css +96 -4
- package/src/lib/forms/switch/switch.component.css +45 -15
- package/src/lib/forms/tag-input/tag-input.component.css +2 -2
- package/src/lib/forms/textarea/textarea-autosize-styles.component.css +2 -3
- package/src/lib/forms/textarea/textarea.component.css +7 -2
- package/src/lib/grid/grid-item-toolbar.component.css +10 -0
- package/src/lib/grid/grid-item.component.css +6 -1
- package/src/lib/kbd/kbd.component.css +1 -1
- package/src/lib/loader/brand-loader/brand-loader.component.css +4 -1
- package/src/lib/loader/progress-bar/progress-bar.component.css +1 -1
- package/src/lib/loader/spinner/spinner-styles.component.css +1 -1
- package/src/lib/match/match-card.component.css +9 -3
- package/src/lib/match/match-score.component.css +4 -1
- package/src/lib/menu/menu-selection-item.component.css +1 -1
- package/src/lib/menu/menu.component.css +6 -1
- package/src/lib/notification/notification.component.css +3 -3
- package/src/lib/overlay/overlay-container.component.css +4 -0
- package/src/lib/pagination/pagination.component.css +4 -0
- package/src/lib/progress-steps/progress-step.component.css +4 -4
- package/src/lib/query-error/query-error.component.css +4 -0
- package/src/lib/scheduler/scheduler-appointment-styles.component.css +21 -5
- package/src/lib/scheduler/scheduler-month-view.component.css +4 -2
- package/src/lib/scheduler/scheduler-time-grid-view.component.css +8 -2
- package/src/lib/scrollable/headless/scrollable-navigation.component.css +1 -1
- package/src/lib/standings/standings-pick.component.css +1 -1
- package/src/lib/standings/standings.component.css +10 -10
- package/src/lib/stat-tile/stat-tile-sparkline.component.css +1 -1
- package/src/lib/stream/consent/stream-consent.component.css +99 -0
- package/src/lib/stream/error/stream-player-error.component.css +100 -0
- package/src/lib/stream/loading/stream-player-loading.component.css +16 -0
- package/src/lib/stream/pip/pip-slot-placeholder.component.css +68 -0
- package/src/lib/stream/stream-player-slot-styles.component.css +4 -0
- package/src/lib/table/table-resize-grip.component.css +2 -1
- package/src/lib/table/table-select-cell.component.css +45 -15
- package/src/lib/table/table.component.css +15 -5
- package/src/lib/tabs/tab-bar-styles.component.css +1 -1
- package/src/lib/tabs/tabs/tab-group.component.css +111 -0
- package/src/lib/time-picker/time-picker.component.css +2 -1
- package/src/lib/timeline/timeline-item.component.css +5 -1
- package/src/lib/toolbar/toolbar.component.css +4 -0
- package/src/lib/tooltip/tooltip.component.css +14 -0
- package/types/ethlete-components.d.ts +156 -128
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { brandLoader, loaderStyles, progressBar, spinner } from '@ethlete/components/design/loader';
|
|
2
|
+
import { definePart, drawing } from '@design-system';
|
|
3
|
+
import frame from './loader.css?inline';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
form: 'spinner' | 'spinner-determinate' | 'spinner-themed' | 'progress-bar' | 'progress-bar-themed' | 'brand-loader';
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const row = (items: string[]) => `<div class="loader-row">${items.join('')}</div>`;
|
|
10
|
+
|
|
11
|
+
const column = (items: string[]) => `<div class="loader-column">${items.join('')}</div>`;
|
|
12
|
+
|
|
13
|
+
const render = ({ form }: Props) => {
|
|
14
|
+
switch (form) {
|
|
15
|
+
case 'spinner':
|
|
16
|
+
return row([
|
|
17
|
+
spinner(),
|
|
18
|
+
spinner({ diameter: 45, strokeWidth: 2 }),
|
|
19
|
+
spinner({ diameter: 45, strokeWidth: 2, track: true }),
|
|
20
|
+
spinner({ diameter: 72, strokeWidth: 4, track: true }),
|
|
21
|
+
]);
|
|
22
|
+
case 'spinner-determinate':
|
|
23
|
+
return row([
|
|
24
|
+
spinner({ diameter: 45, strokeWidth: 2, track: true, value: 0 }),
|
|
25
|
+
spinner({ diameter: 45, strokeWidth: 2, track: true, value: 65 }),
|
|
26
|
+
spinner({ diameter: 45, strokeWidth: 2, track: true, value: 100 }),
|
|
27
|
+
spinner({ diameter: 45, strokeWidth: 2, value: 65 }),
|
|
28
|
+
]);
|
|
29
|
+
case 'spinner-themed':
|
|
30
|
+
return row([
|
|
31
|
+
spinner({ diameter: 45, strokeWidth: 2, track: true, color: 'brand' }),
|
|
32
|
+
spinner({ diameter: 45, strokeWidth: 2, track: true, value: 65, color: 'brand' }),
|
|
33
|
+
spinner({ diameter: 45, strokeWidth: 2, track: true, color: 'danger' }),
|
|
34
|
+
spinner({ diameter: 45, strokeWidth: 2, track: true, color: 'warning' }),
|
|
35
|
+
]);
|
|
36
|
+
case 'progress-bar':
|
|
37
|
+
return column([progressBar({ value: 42 }), progressBar({ value: 100 }), progressBar()]);
|
|
38
|
+
case 'progress-bar-themed':
|
|
39
|
+
return column([
|
|
40
|
+
progressBar({ value: 42, color: 'brand' }),
|
|
41
|
+
progressBar({ color: 'brand' }),
|
|
42
|
+
progressBar({ value: 42, color: 'danger' }),
|
|
43
|
+
progressBar({ value: 42, color: 'warning' }),
|
|
44
|
+
]);
|
|
45
|
+
case 'brand-loader':
|
|
46
|
+
return row([brandLoader()]);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default definePart({
|
|
51
|
+
note: 'Decided rules only',
|
|
52
|
+
madeBy: ['claude-opus-5-5'],
|
|
53
|
+
states: {
|
|
54
|
+
spinner: { form: 'spinner' },
|
|
55
|
+
'spinner-determinate': { form: 'spinner-determinate' },
|
|
56
|
+
'spinner-themed': { form: 'spinner-themed' },
|
|
57
|
+
'progress-bar': { form: 'progress-bar' },
|
|
58
|
+
'progress-bar-themed': { form: 'progress-bar-themed' },
|
|
59
|
+
'brand-loader': { form: 'brand-loader' },
|
|
60
|
+
},
|
|
61
|
+
checks: { skip: ['CLIPPED'] },
|
|
62
|
+
widths: [480],
|
|
63
|
+
frame: (body) => `<div class="loader-frame">${body}</div>`,
|
|
64
|
+
render: (props: Props) =>
|
|
65
|
+
drawing({
|
|
66
|
+
body: render(props),
|
|
67
|
+
styles: [loaderStyles, frame].join('\n'),
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { masonry, MasonryMockItem, masonryStyles } from '@ethlete/components/design/masonry';
|
|
2
|
+
import { definePart, drawing } from '@design-system';
|
|
3
|
+
import frame from './masonry.css?inline';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
items: 'rest' | 'uneven' | 'few';
|
|
7
|
+
width?: number;
|
|
8
|
+
columnWidth?: number;
|
|
9
|
+
dir?: 'rtl';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const tiles = (heights: readonly number[]): MasonryMockItem[] =>
|
|
13
|
+
heights.map((height, index) => ({ title: `Card ${index + 1}`, subtitle: `Height ${height}`, height }));
|
|
14
|
+
|
|
15
|
+
const ITEMS = {
|
|
16
|
+
rest: tiles([120, 160, 140, 180, 120, 150, 140, 110]),
|
|
17
|
+
uneven: tiles([80, 260, 120, 60, 200, 90, 170, 70, 130]),
|
|
18
|
+
few: tiles([120, 160, 100]),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default definePart({
|
|
22
|
+
note: "Today's look",
|
|
23
|
+
madeBy: ['claude-opus-5-5'],
|
|
24
|
+
states: {
|
|
25
|
+
rest: { items: 'rest', columnWidth: 130 },
|
|
26
|
+
uneven: { items: 'uneven', columnWidth: 130 },
|
|
27
|
+
narrow: { items: 'rest', columnWidth: 130, width: 280 },
|
|
28
|
+
'single-column': { items: 'few' },
|
|
29
|
+
rtl: { items: 'rest', columnWidth: 130, dir: 'rtl' },
|
|
30
|
+
},
|
|
31
|
+
widths: [480],
|
|
32
|
+
frame: (body) => `<div class="t-frame">${body}</div>`,
|
|
33
|
+
render: (props: Props) =>
|
|
34
|
+
drawing({
|
|
35
|
+
body: masonry({
|
|
36
|
+
items: ITEMS[props.items],
|
|
37
|
+
width: props.width,
|
|
38
|
+
columnWidth: props.columnWidth,
|
|
39
|
+
dir: props.dir,
|
|
40
|
+
}),
|
|
41
|
+
styles: [masonryStyles, frame].join('\n'),
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
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-match-card.et-focus-ring {
|
|
11
|
+
--_et-focus-ring-color: var(--et-theme-color-ink-solid);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.et-match-card:where([data-winner='home']) .et-match-card-side:where([data-side='home']),
|
|
15
|
+
.et-match-card:where([data-winner='away']) .et-match-card-side:where([data-side='away']) {
|
|
16
|
+
--_et-match-card-side-marker-color: light-dark(
|
|
17
|
+
var(--et-theme-color-ink-solid),
|
|
18
|
+
var(--et-theme-color-primary-solid)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.et-match-card-live-dot {
|
|
23
|
+
background: light-dark(var(--et-theme-color-ink-solid), var(--et-theme-color-primary-solid));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { matchCard, MatchMockOptions, matchStyles } from '@ethlete/components/design/match';
|
|
2
|
+
import { definePart, drawing } from '@design-system';
|
|
3
|
+
import frame from './match.css?inline';
|
|
4
|
+
|
|
5
|
+
type Props = { card: MatchMockOptions };
|
|
6
|
+
|
|
7
|
+
const HOME = { name: 'Northbridge Rovers', code: 'NBR', subtitle: 'Northbridge', seed: 1 };
|
|
8
|
+
const AWAY = { name: 'Harbor City', code: 'HBC', subtitle: 'Harbor', seed: 4 };
|
|
9
|
+
|
|
10
|
+
const finished: MatchMockOptions = {
|
|
11
|
+
home: HOME,
|
|
12
|
+
away: AWAY,
|
|
13
|
+
status: 'finished',
|
|
14
|
+
homeScore: 2,
|
|
15
|
+
awayScore: 1,
|
|
16
|
+
winnerSide: 'home',
|
|
17
|
+
label: 'Matchday 12',
|
|
18
|
+
startTime: '10/31/2026 8:00 PM',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default definePart({
|
|
22
|
+
note: 'scale of the decided shape, Q182, lead merge',
|
|
23
|
+
madeBy: ['claude-opus-5-5'],
|
|
24
|
+
states: {
|
|
25
|
+
finished: { card: finished },
|
|
26
|
+
live: {
|
|
27
|
+
card: {
|
|
28
|
+
home: HOME,
|
|
29
|
+
away: AWAY,
|
|
30
|
+
status: 'live',
|
|
31
|
+
homeScore: 1,
|
|
32
|
+
awayScore: 1,
|
|
33
|
+
label: 'Matchday 12',
|
|
34
|
+
liveColor: 'danger',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
scheduled: {
|
|
38
|
+
card: { home: HOME, away: AWAY, status: 'scheduled', label: 'Matchday 13', startTime: '11/07/2026 6:30 PM' },
|
|
39
|
+
},
|
|
40
|
+
series: {
|
|
41
|
+
card: {
|
|
42
|
+
home: HOME,
|
|
43
|
+
away: AWAY,
|
|
44
|
+
status: 'finished',
|
|
45
|
+
homeScore: 1,
|
|
46
|
+
awayScore: 2,
|
|
47
|
+
winnerSide: 'away',
|
|
48
|
+
gameScores: [
|
|
49
|
+
{ home: 13, away: 11 },
|
|
50
|
+
{ home: 9, away: 13 },
|
|
51
|
+
{ home: 10, away: 13 },
|
|
52
|
+
],
|
|
53
|
+
label: 'Semi-final, best of 3',
|
|
54
|
+
showSeeds: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
outcome: {
|
|
58
|
+
card: { home: HOME, away: AWAY, status: 'finished', resultKind: 'outcome', winnerSide: 'away', label: 'Round 3' },
|
|
59
|
+
},
|
|
60
|
+
tbd: {
|
|
61
|
+
card: { home: HOME, status: 'scheduled', label: 'Final', startTime: '11/21/2026 7:00 PM' },
|
|
62
|
+
},
|
|
63
|
+
compact: { card: { ...finished, size: 'compact' } },
|
|
64
|
+
expanded: { card: { ...finished, size: 'expanded' } },
|
|
65
|
+
wide: { card: { ...finished, size: 'wide' } },
|
|
66
|
+
hover: { card: { ...finished, interactive: true, force: 'hover' } },
|
|
67
|
+
'focus-visible': { card: { ...finished, interactive: true, force: 'focus-visible' } },
|
|
68
|
+
},
|
|
69
|
+
widths: [280, 480, 720],
|
|
70
|
+
frame: (body) => `<div class="t-frame">${body}</div>`,
|
|
71
|
+
render: (props: Props) =>
|
|
72
|
+
drawing({
|
|
73
|
+
// Lib bug: et-match-score clips its 25px digit box to 24px (overflow: hidden at line-height 1.2), reported.
|
|
74
|
+
body: matchCard(props.card),
|
|
75
|
+
styles: [matchStyles, frame].join('\n'),
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.menu-frame {
|
|
2
|
+
display: flex;
|
|
3
|
+
padding: 0.8rem;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.menu-frame .et-menu {
|
|
7
|
+
--et-menu-max-height: 100rem;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@layer components {
|
|
11
|
+
.et-menu .et-menu-selection-item:not(.et-menu-selection-item--has-icon) .et-menu-item-check,
|
|
12
|
+
.et-menu .et-menu-selection-item--has-icon:is([aria-checked='true'], [aria-checked='mixed']) .et-menu-item-check {
|
|
13
|
+
color: var(--et-theme-color-ink-solid);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@layer components {
|
|
18
|
+
.et-menu .et-menu-item[data-active]:focus-visible {
|
|
19
|
+
outline: 2px solid var(--et-theme-color-ink-solid);
|
|
20
|
+
outline-offset: -2px;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
menu,
|
|
3
|
+
type MenuMockItem,
|
|
4
|
+
type MenuMockRow,
|
|
5
|
+
type MenuMockSelectionItem,
|
|
6
|
+
menuStyles,
|
|
7
|
+
} from '@ethlete/components/design/menu';
|
|
8
|
+
import { definePart, drawing, interactionRows } from '@design-system';
|
|
9
|
+
import edge from './menu.css?inline';
|
|
10
|
+
|
|
11
|
+
type ItemInteraction = 'hover' | 'active' | 'focus-visible';
|
|
12
|
+
|
|
13
|
+
type Props = {
|
|
14
|
+
rows: MenuMockRow[];
|
|
15
|
+
forced?: ItemInteraction;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const MARK = 'data-et-control="item"';
|
|
19
|
+
|
|
20
|
+
const row = (label: string, extra: Partial<Extract<MenuMockRow, { label: string }>> = {}) =>
|
|
21
|
+
({ label, attributes: MARK, ...extra }) as MenuMockRow;
|
|
22
|
+
|
|
23
|
+
const FULL: MenuMockRow[] = [
|
|
24
|
+
{ kind: 'label', label: 'Document' },
|
|
25
|
+
row('Rename', { shortcut: 'F2' }),
|
|
26
|
+
row('Duplicate', { shortcut: 'Ctrl D' }),
|
|
27
|
+
row('Move to', { submenu: true }),
|
|
28
|
+
row('Export as PDF', { disabled: true }),
|
|
29
|
+
{ kind: 'separator' },
|
|
30
|
+
{
|
|
31
|
+
kind: 'checkbox-group',
|
|
32
|
+
label: 'Show',
|
|
33
|
+
items: [
|
|
34
|
+
{ kind: 'checkbox', label: 'Comments', checked: true, attributes: MARK },
|
|
35
|
+
{ kind: 'checkbox', label: 'Suggestions', attributes: MARK },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{ kind: 'separator' },
|
|
39
|
+
{
|
|
40
|
+
kind: 'radio-group',
|
|
41
|
+
label: 'Sort by',
|
|
42
|
+
items: [
|
|
43
|
+
{ kind: 'radio', label: 'Name', checked: true, attributes: MARK },
|
|
44
|
+
{ kind: 'radio', label: 'Date', attributes: MARK },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{ kind: 'separator' },
|
|
48
|
+
row('Delete', { destructive: true }),
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const isItemRow = (entry: MenuMockRow): entry is MenuMockItem | MenuMockSelectionItem =>
|
|
52
|
+
entry.kind === undefined || entry.kind === 'item' || entry.kind === 'checkbox' || entry.kind === 'radio';
|
|
53
|
+
|
|
54
|
+
const withForced = (rows: MenuMockRow[], forced?: ItemInteraction): MenuMockRow[] =>
|
|
55
|
+
rows.map((entry, index) =>
|
|
56
|
+
forced && index === 1 && isItemRow(entry)
|
|
57
|
+
? {
|
|
58
|
+
...entry,
|
|
59
|
+
active: true,
|
|
60
|
+
activeSource: forced === 'focus-visible' ? 'keyboard' : entry.activeSource,
|
|
61
|
+
attributes: [entry.attributes, `data-et-force="${forced}"`].filter(Boolean).join(' '),
|
|
62
|
+
}
|
|
63
|
+
: entry,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const { states, grid } = interactionRows(
|
|
67
|
+
{
|
|
68
|
+
item: { rows: [row('Rename'), row('Duplicate', { shortcut: 'Ctrl D' }), row('Move to', { submenu: true })] },
|
|
69
|
+
checked: {
|
|
70
|
+
rows: [
|
|
71
|
+
{ kind: 'checkbox', label: 'Suggestions', attributes: MARK },
|
|
72
|
+
{ kind: 'checkbox', label: 'Comments', checked: true, attributes: MARK },
|
|
73
|
+
{ kind: 'radio', label: 'Name', checked: true, attributes: MARK },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
} satisfies Record<string, Props>,
|
|
77
|
+
{
|
|
78
|
+
interactions: ['hover', 'active', 'focus-visible'],
|
|
79
|
+
force: (props: Props, interaction: ItemInteraction): Props => ({ ...props, forced: interaction }),
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
export default definePart({
|
|
84
|
+
note: 'Decided rules only',
|
|
85
|
+
madeBy: ['claude-opus-5-5'],
|
|
86
|
+
states: {
|
|
87
|
+
menu: { rows: FULL },
|
|
88
|
+
...states,
|
|
89
|
+
disabled: { rows: [row('Rename'), row('Export as PDF', { disabled: true }), row('Duplicate')] },
|
|
90
|
+
'submenu-open': { rows: [row('Rename'), row('Move to', { submenu: 'open', active: true }), row('Duplicate')] },
|
|
91
|
+
},
|
|
92
|
+
grid,
|
|
93
|
+
widths: [480],
|
|
94
|
+
controls: {
|
|
95
|
+
item: { kind: 'row', input: ['pointer', 'keyboard'], disabled: true },
|
|
96
|
+
},
|
|
97
|
+
frame: (body) => `<div class="menu-frame">${body}</div>`,
|
|
98
|
+
render: ({ rows, forced }: Props) =>
|
|
99
|
+
drawing({
|
|
100
|
+
body: menu({ rows: withForced(rows, forced), color: 'brand' }),
|
|
101
|
+
styles: [menuStyles, edge].join('\n'),
|
|
102
|
+
}),
|
|
103
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.notification-frame {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: flex-end;
|
|
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
|
+
.et-notification {
|
|
12
|
+
border-inline-start-color: var(--et-theme-color-ink-solid);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.et-notification .et-notification-icon {
|
|
16
|
+
color: var(--et-theme-color-ink-solid);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.et-notification .et-notification-spinner {
|
|
20
|
+
--et-spinner-color: var(--et-theme-color-ink-solid);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.et-notification .et-focus-ring {
|
|
24
|
+
--_et-focus-ring-color: var(--et-theme-color-ink-solid);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@layer components {
|
|
29
|
+
.et-notification-dismiss-btn:focus-visible {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { notification, notificationStack, notificationStyles } from '@ethlete/components/design/notification';
|
|
2
|
+
import { definePart, drawing } from '@design-system';
|
|
3
|
+
import edge from './notification.css?inline';
|
|
4
|
+
|
|
5
|
+
type Interaction = 'hover' | 'active' | 'focus-visible';
|
|
6
|
+
|
|
7
|
+
type Status = 'success' | 'error' | 'info' | 'loading';
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
status: Status | 'stack';
|
|
11
|
+
close?: Interaction;
|
|
12
|
+
action?: Interaction;
|
|
13
|
+
secondary?: Interaction;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const PANE = 'data-et-surface-fill';
|
|
17
|
+
|
|
18
|
+
const mark = (control: string, interaction?: Interaction) =>
|
|
19
|
+
[`data-et-control="${control}" data-can-animate`, interaction && `data-et-force="${interaction}"`]
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.join(' ');
|
|
22
|
+
|
|
23
|
+
const draw = (status: Status, { close, action, secondary }: Omit<Props, 'status'> = {}) => {
|
|
24
|
+
const controls = {
|
|
25
|
+
dismissAttributes: mark('close', close),
|
|
26
|
+
attributes: PANE,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
switch (status) {
|
|
30
|
+
case 'success':
|
|
31
|
+
return notification({
|
|
32
|
+
status,
|
|
33
|
+
title: 'Kick-off moved',
|
|
34
|
+
message: 'Both teams and the referee get an email with the new time.',
|
|
35
|
+
action: { label: 'Undo', attributes: mark('action', action) },
|
|
36
|
+
...controls,
|
|
37
|
+
});
|
|
38
|
+
case 'error':
|
|
39
|
+
return notification({
|
|
40
|
+
status,
|
|
41
|
+
color: 'danger',
|
|
42
|
+
title: 'Result not saved',
|
|
43
|
+
message: 'The connection dropped before the server answered.',
|
|
44
|
+
action: { label: 'Retry', attributes: mark('action', action) },
|
|
45
|
+
secondaryAction: { label: 'Details', attributes: mark('secondary', secondary) },
|
|
46
|
+
...controls,
|
|
47
|
+
});
|
|
48
|
+
case 'info':
|
|
49
|
+
return notification({
|
|
50
|
+
status,
|
|
51
|
+
title: 'Round 4 is open',
|
|
52
|
+
message: 'Predictions close at kick-off of the first match.',
|
|
53
|
+
...controls,
|
|
54
|
+
});
|
|
55
|
+
case 'loading':
|
|
56
|
+
return notification({
|
|
57
|
+
status,
|
|
58
|
+
title: 'Uploading 3 files',
|
|
59
|
+
message: 'You can keep working while this runs.',
|
|
60
|
+
...controls,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const render = ({ status, ...interactions }: Props) =>
|
|
66
|
+
status === 'stack'
|
|
67
|
+
? notificationStack((['loading', 'info', 'error', 'success'] as const).map((each) => draw(each)))
|
|
68
|
+
: draw(status, interactions);
|
|
69
|
+
|
|
70
|
+
export default definePart({
|
|
71
|
+
note: 'The SDK notification (et-notification), drawn by its mock from @ethlete/components/design: one notification per status with title, message, dismiss icon button and, where the status has them, a primary and a secondary text-button action; the four statuses in the stack column. The pane sits one surface level above the page, as the component forces elevation 1. Error takes the danger theme (the semantic error theme); success, info and loading take the theme around them, since the design product registers no success theme.',
|
|
72
|
+
madeBy: ['claude-opus-5-5'],
|
|
73
|
+
states: {
|
|
74
|
+
success: { status: 'success' },
|
|
75
|
+
error: { status: 'error' },
|
|
76
|
+
info: { status: 'info' },
|
|
77
|
+
loading: { status: 'loading' },
|
|
78
|
+
'close-hover': { status: 'error', close: 'hover' },
|
|
79
|
+
'close-focus-visible': { status: 'error', close: 'focus-visible' },
|
|
80
|
+
'action-focus-visible': { status: 'error', action: 'focus-visible' },
|
|
81
|
+
'secondary-focus-visible': { status: 'error', secondary: 'focus-visible' },
|
|
82
|
+
'success-close-focus-visible': { status: 'success', close: 'focus-visible' },
|
|
83
|
+
'success-action-focus-visible': { status: 'success', action: 'focus-visible' },
|
|
84
|
+
stack: { status: 'stack' },
|
|
85
|
+
},
|
|
86
|
+
widths: [480],
|
|
87
|
+
contrast: {
|
|
88
|
+
exceptions: [
|
|
89
|
+
{
|
|
90
|
+
selector: '.et-notification-message',
|
|
91
|
+
reason:
|
|
92
|
+
"Today the message is the muted surface colour at 0.65 opacity; its contrast is an open point, not this round's question.",
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
controls: {
|
|
97
|
+
close: { kind: 'button', input: ['pointer', 'keyboard'] },
|
|
98
|
+
action: { kind: 'button', input: ['pointer', 'keyboard'] },
|
|
99
|
+
secondary: { kind: 'button', input: ['pointer', 'keyboard'] },
|
|
100
|
+
},
|
|
101
|
+
frame: (body) => `<div class="notification-frame">${body}</div>`,
|
|
102
|
+
render: (props: Props) =>
|
|
103
|
+
drawing({
|
|
104
|
+
body: render(props),
|
|
105
|
+
styles: [notificationStyles, edge].join('\n'),
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.t-frame {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
display: grid;
|
|
4
|
+
justify-items: start;
|
|
5
|
+
gap: 1.6rem;
|
|
6
|
+
padding: 1.6rem;
|
|
7
|
+
background: var(--et-surface-background-solid);
|
|
8
|
+
color: var(--et-surface-color-solid);
|
|
9
|
+
font-size: 1.4rem;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@layer components {
|
|
13
|
+
et-otp-input .et-otp-input-segment[data-caret] {
|
|
14
|
+
border-color: var(--et-theme-color-ink-solid);
|
|
15
|
+
outline-color: var(--et-theme-color-ink-solid);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
et-otp-input .et-otp-input-segment .et-otp-input-caret {
|
|
19
|
+
background: var(--et-theme-color-ink-solid);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
et-otp-input > et-label .et-label-required-marker {
|
|
23
|
+
color: light-dark(var(--et-theme-color-ink-solid), var(--et-theme-color-primary-solid));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@media (hover: hover) {
|
|
27
|
+
et-otp-input:not(:has(.et-otp-input-native:read-only)):hover .et-otp-input-segment:not([data-caret]) {
|
|
28
|
+
border-color: var(--et-surface-interaction-hover-solid);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
et-otp-input[data-error]:not(:has(.et-otp-input-native:read-only)):hover .et-otp-input-segment:not([data-caret]) {
|
|
32
|
+
border-color: var(--et-theme-color-ink-solid);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { otpInput, type OtpInputMockOptions, otpInputStyles } from '@ethlete/components/design/otp-input';
|
|
2
|
+
import { definePart, drawing } from '@design-system';
|
|
3
|
+
import frame from './otp-input.css?inline';
|
|
4
|
+
|
|
5
|
+
type Props = { field: OtpInputMockOptions };
|
|
6
|
+
|
|
7
|
+
const code = (extra: Partial<OtpInputMockOptions> = {}): Props => ({
|
|
8
|
+
field: { id: 'code', label: 'Verification code', ...extra },
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const pin = (extra: Partial<OtpInputMockOptions> = {}): Props => ({
|
|
12
|
+
field: { id: 'pin', label: 'PIN', length: 4, masked: true, ...extra },
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const INVALID = {
|
|
16
|
+
required: true,
|
|
17
|
+
error: 'Enter the code from the SMS.',
|
|
18
|
+
errorColor: 'danger',
|
|
19
|
+
} satisfies Partial<OtpInputMockOptions>;
|
|
20
|
+
|
|
21
|
+
export default definePart({
|
|
22
|
+
note: 'The SDK otp input (et-otp-input), first drawing, drawn by the mock from @ethlete/components/design/otp-input with the real markup and CSS: et-label with the required marker, the segment row, the invisible native input over it, et-form-support with hint and error; the brand theme, danger as the error theme. The field has no :focus or :hover CSS of its own: focus is the data-caret segment (a 1px border plus a 1px inset outline in primary, and a blinking 2px caret bar in an empty one), which the mock draws with focused. Fixtures are fictional: Verification code, 6 digits 482193, hint We sent it to +49 151 0000 0000., required with error Enter the code from the SMS.; a 4-digit masked PIN. Framed as a page with 16px padding at 320, 360 and 640. States: empty, partial, filled, hover, focus-empty, focus-partial, focus-filled, hint, required, invalid, invalid-partial, invalid-focus, disabled, disabled-filled, readonly, readonly-focus, pin-partial and pin-focus. Numbers (light / dark), from shot pixels at scale 2 at 360, brand mint, a: segment 44px outer (border-box, fixed in 00072d935), gap 8px, row 304px, digits 19.8px; empty and filled segment border (surface border) vs page 1.26 / 1.73, the only edge of a segment; caret segment edge and caret bar (primary) 1.33 / 13.50; required marker (brand primary) 1.33 / 13.50; invalid border (danger) 4.83 / 3.71; label (muted) 7.81 / 6.94; disabled digits 2.55 / 3.72 (opacity 0.4, exempt). Decided rules that arise: Q88 (focus in ink: the caret segment edge and the caret bar take ink, the invalid one the danger ink) and Q99a (a mint mark on light takes ink: the required marker light-dark(ink, primary)). Hover follows the text field (form-field-text-shell: the frame border to surface-interaction-hover, the error frame to ink, none when readonly or disabled), on the host since the host is the click target (otp.activate()). The filled segment edge in c is my proposal, not a decided rule (Q103). Findings for later, lib bugs apart: (L1) a 6-digit field needed 304px and never shrank; at 320 with 16px page padding it overflowed by 16px (drawn at 320). Now fixed: the segments shrink, 41.3px at 320, and the render at 640 is unchanged. (L2) a readonly field showed the focus caret segment and, when not full, the blinking caret bar, though nothing can be typed (readonly-focus). Now fixed: no caret when readonly. Design: (1) WCAG 1.4.11: the segment border 1.26 / 1.73 is the only edge of an empty segment, as with the checkbox box and the radio circle. Not drawn. (2) A filled segment looks like an empty one apart from its digit; c answers it. (3) The error border is the full danger primary, while the text field draws its error frame at 50 % danger; kept, it passes 3:1. (4) Hover (10.37 / 13.07) outshines the focus edge (5.48 / 13.50) in b and c, as it does on the text field. (5) The masked dot is a 19.8px bullet, about 5px wide. (6) The error marker 3.71 in dark, exempted as in checkbox r1. Later rounds, left out here: data-warning (no CSS of its own), a coloured field, alphanumeric codes, other lengths and segment sizes via the custom properties, RTL and the pop-in animation.',
|
|
23
|
+
madeBy: ['claude-opus-5-5'],
|
|
24
|
+
states: {
|
|
25
|
+
empty: code(),
|
|
26
|
+
partial: code({ value: '482' }),
|
|
27
|
+
filled: code({ value: '482193' }),
|
|
28
|
+
hover: code({ value: '482', force: 'hover' }),
|
|
29
|
+
'focus-empty': code({ focused: true }),
|
|
30
|
+
'focus-partial': code({ value: '482', focused: true }),
|
|
31
|
+
'focus-filled': code({ value: '482193', focused: true }),
|
|
32
|
+
hint: code({ hint: 'We sent it to +49 151 0000 0000.' }),
|
|
33
|
+
required: code({ required: true, hint: 'We sent it to +49 151 0000 0000.' }),
|
|
34
|
+
invalid: code(INVALID),
|
|
35
|
+
'invalid-partial': code({ ...INVALID, value: '48' }),
|
|
36
|
+
'invalid-focus': code({ ...INVALID, value: '48', focused: true }),
|
|
37
|
+
disabled: code({ disabled: true }),
|
|
38
|
+
'disabled-filled': code({ value: '482193', disabled: true }),
|
|
39
|
+
readonly: code({ value: '482193', readonly: true }),
|
|
40
|
+
'readonly-focus': code({ value: '482', readonly: true, focused: true }),
|
|
41
|
+
'pin-partial': pin({ value: '73' }),
|
|
42
|
+
'pin-focus': pin({ value: '73', focused: true }),
|
|
43
|
+
},
|
|
44
|
+
contrast: {
|
|
45
|
+
exceptions: [
|
|
46
|
+
{
|
|
47
|
+
selector: 'et-otp-input:has(.et-otp-input-native:disabled) .et-otp-input-segment-char',
|
|
48
|
+
reason:
|
|
49
|
+
'A disabled field is exempt under WCAG 1.4.3 (inactive component): opacity 0.4 draws the digits 2.55:1 / 3.72:1.',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
selector: 'et-otp-input[data-error] .et-label-required-marker',
|
|
53
|
+
reason:
|
|
54
|
+
'The required marker takes the error theme primary: 3.71:1 in dark, under 4.5:1. An open point (checkbox r1 finding 3), not a decided rule.',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
widths: [320, 360, 640],
|
|
59
|
+
frame: (body) => `<div class="t-frame">${body}</div>`,
|
|
60
|
+
render: (props: Props) =>
|
|
61
|
+
drawing({
|
|
62
|
+
body: otpInput(props.field),
|
|
63
|
+
styles: [otpInputStyles, frame].join('\n'),
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
.pagination-frame {
|
|
2
|
+
display: flex;
|
|
3
|
+
padding: 0.8rem;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
@layer components {
|
|
7
|
+
.et-pagination-host .et-pagination-button {
|
|
8
|
+
--_et-focus-ring-color: var(--et-theme-color-ink-solid);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.et-pagination-host .et-pagination-button[aria-current='page'] {
|
|
12
|
+
--et-button-shadow: inset 0 0 0 1px var(--et-theme-color-ink-solid);
|
|
13
|
+
}
|
|
14
|
+
}
|