@med-o/ui 0.0.0-stage → 0.3.0

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -2
  3. package/dist/index.d.ts +2237 -0
  4. package/dist/index.js +5119 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/styles.css +1 -0
  7. package/package.json +77 -4
  8. package/src/accordion/accordion.css +184 -0
  9. package/src/alert/alert.css +147 -0
  10. package/src/avatar/avatar.css +221 -0
  11. package/src/badge/badge.css +153 -0
  12. package/src/bottom-sheet/bottom-sheet.css +202 -0
  13. package/src/breadcrumbs/breadcrumbs.css +147 -0
  14. package/src/bulk-action-bar/bulk-action-bar.css +98 -0
  15. package/src/button/button.css +319 -0
  16. package/src/card/card.css +217 -0
  17. package/src/checkbox/choice.css +196 -0
  18. package/src/combobox/combobox.css +125 -0
  19. package/src/date-time/date-time.css +474 -0
  20. package/src/dialog/dialog.css +319 -0
  21. package/src/drawer/drawer.css +156 -0
  22. package/src/empty-state/empty-state.css +161 -0
  23. package/src/field/field.css +115 -0
  24. package/src/link/link.css +103 -0
  25. package/src/list/list.css +172 -0
  26. package/src/list-view/list-view.css +473 -0
  27. package/src/listbox/listbox.css +203 -0
  28. package/src/media/media.css +298 -0
  29. package/src/menu/menu.css +183 -0
  30. package/src/navigation/navigation.css +625 -0
  31. package/src/pagination/pagination.css +132 -0
  32. package/src/popover/popover.css +166 -0
  33. package/src/progress/progress.css +225 -0
  34. package/src/search/search.css +309 -0
  35. package/src/select/select.css +229 -0
  36. package/src/slider/slider.css +212 -0
  37. package/src/styles.css +48 -0
  38. package/src/switch/switch.css +160 -0
  39. package/src/table/table.css +286 -0
  40. package/src/tabs/tabs.css +211 -0
  41. package/src/tag/tag.css +225 -0
  42. package/src/text-field/input.css +206 -0
  43. package/src/toast/toast.css +184 -0
  44. package/src/toggle-button/toggle-button.css +143 -0
  45. package/src/tooltip/tooltip.css +66 -0
  46. package/src/upload/upload.css +380 -0
  47. package/src/utils/utilities.css +14 -0
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Slider – med.o Design System
3
+ *
4
+ * <div class="medo-slider" [data-disabled]>
5
+ * <div class="medo-slider__header"><label>Volume</label><output>40 %</output></div>
6
+ * <div class="medo-slider__track"> 20 high, 10 px inset on both ends
7
+ * rail (4, border/default) · ticks (2 × 6, border/strong) · fill (action/primary)
8
+ * <div class="medo-slider__thumb" [data-hovered] [data-dragging] [data-focus-visible]> 20 · hit area 44
9
+ * </div>
10
+ * <div class="medo-slider__limits">0 · 100</div>
11
+ * </div>
12
+ * Halo: hover 36 state/hover · pressed 40 state/pressed. Focus: 2 px focus/ring, 28 ring. Disabled 50 %.
13
+ */
14
+
15
+ .medo-slider {
16
+ display: grid;
17
+ gap: var(--medo-space-2);
18
+ inline-size: 100%;
19
+ min-inline-size: 0;
20
+ }
21
+
22
+ .medo-slider[data-disabled] {
23
+ opacity: 0.5;
24
+ }
25
+
26
+ .medo-slider__header {
27
+ display: flex;
28
+ align-items: baseline;
29
+ justify-content: space-between;
30
+ gap: var(--medo-space-3);
31
+ }
32
+
33
+ .medo-slider__label {
34
+ font: var(--medo-typography-label-md);
35
+ color: var(--medo-color-text-primary);
36
+ }
37
+
38
+ .medo-slider__value {
39
+ font: var(--medo-typography-body-sm);
40
+ font-variant-numeric: tabular-nums;
41
+ color: var(--medo-color-text-secondary);
42
+ white-space: nowrap;
43
+ }
44
+
45
+ /* The track is inset by 10 px, so the thumb never overflows the field; pointer positions map to it. */
46
+ .medo-slider__track {
47
+ position: relative;
48
+ block-size: var(--medo-space-5); /* 20 */
49
+ margin-inline: calc(var(--medo-space-2) + var(--medo-space-0-5)); /* 10 */
50
+ cursor: pointer;
51
+ }
52
+
53
+ .medo-slider[data-disabled] .medo-slider__track {
54
+ cursor: not-allowed;
55
+ }
56
+
57
+ .medo-slider__rail,
58
+ .medo-slider__fill {
59
+ position: absolute;
60
+ inset-block-start: 50%;
61
+ block-size: var(--medo-space-1); /* 4 */
62
+ border-radius: var(--medo-radius-full);
63
+ translate: 0 -50%;
64
+ }
65
+
66
+ .medo-slider__rail {
67
+ inset-inline: 0;
68
+ background-color: var(--medo-color-border-default);
69
+ }
70
+
71
+ .medo-slider__fill {
72
+ background-color: var(--medo-color-action-primary);
73
+ }
74
+
75
+ .medo-slider__ticks {
76
+ position: absolute;
77
+ inset-inline: 0;
78
+ inset-block-start: calc(var(--medo-space-4)); /* 16: below the track */
79
+ }
80
+
81
+ .medo-slider__tick {
82
+ position: absolute;
83
+ inline-size: var(--medo-space-0-5);
84
+ block-size: var(--medo-space-1-5);
85
+ border-radius: var(--medo-radius-full);
86
+ background-color: var(--medo-color-border-strong);
87
+ translate: -50% 0;
88
+ }
89
+
90
+ .medo-slider__thumb {
91
+ /* React Aria positions the thumb (left, top 50 %, translate -50 %). */
92
+ inset-block-start: 50%;
93
+ box-sizing: border-box;
94
+ inline-size: var(--medo-space-5); /* 20 */
95
+ block-size: var(--medo-space-5);
96
+ border: var(--medo-border-width-thick) solid var(--medo-color-action-primary);
97
+ border-radius: var(--medo-radius-full);
98
+ background-color: var(--medo-color-surface-default);
99
+ box-shadow: var(--medo-shadow-elevation-1);
100
+ transition:
101
+ box-shadow var(--medo-motion-duration-fast) var(--medo-motion-easing-standard),
102
+ left var(--medo-motion-spring-snappy-duration) var(--medo-motion-spring-snappy),
103
+ right var(--medo-motion-spring-snappy-duration) var(--medo-motion-spring-snappy);
104
+ }
105
+
106
+ /* 44 px hit area around the 20 px thumb. */
107
+ .medo-slider__thumb::before {
108
+ content: '';
109
+ position: absolute;
110
+ inset: calc(-1 * var(--medo-space-3));
111
+ border-radius: var(--medo-radius-full);
112
+ }
113
+
114
+ .medo-slider__thumb[data-hovered] {
115
+ box-shadow:
116
+ 0 0 0 var(--medo-space-2) var(--medo-color-state-hover),
117
+ var(--medo-shadow-elevation-1);
118
+ }
119
+
120
+ .medo-slider__thumb[data-dragging] {
121
+ box-shadow:
122
+ 0 0 0 calc(var(--medo-space-2) + var(--medo-space-0-5)) var(--medo-color-state-pressed),
123
+ var(--medo-shadow-elevation-1);
124
+ }
125
+
126
+ .medo-slider__thumb[data-focus-visible] {
127
+ outline: var(--medo-focus-ring-width) solid var(--medo-color-focus-ring);
128
+ outline-offset: var(--medo-space-0-5);
129
+ }
130
+
131
+ .medo-slider__thumb[data-disabled] {
132
+ cursor: not-allowed;
133
+ }
134
+
135
+ /* Value bubble while dragging – the Tooltip look, above the thumb. */
136
+ .medo-slider__bubble {
137
+ position: absolute;
138
+ inset-block-end: calc(100% + var(--medo-space-2));
139
+ inset-inline-start: 50%;
140
+ translate: -50% 0;
141
+ white-space: nowrap;
142
+ pointer-events: none;
143
+ }
144
+
145
+ .medo-slider__limits {
146
+ display: flex;
147
+ justify-content: space-between;
148
+ font: var(--medo-typography-caption);
149
+ font-variant-numeric: tabular-nums;
150
+ color: var(--medo-color-text-secondary);
151
+ }
152
+
153
+ /* ---------- Slider field ---------- */
154
+
155
+ .medo-slider-field {
156
+ display: grid;
157
+ gap: var(--medo-space-2);
158
+ }
159
+
160
+ /* Single: input 88 on the right, level with the track. */
161
+ .medo-slider-field--single {
162
+ grid-template-columns: 1fr 5.5rem;
163
+ align-items: end;
164
+ gap: var(--medo-space-4);
165
+ }
166
+
167
+ .medo-slider-field__inputs {
168
+ display: grid;
169
+ grid-template-columns: 1fr auto 1fr;
170
+ align-items: center;
171
+ gap: var(--medo-space-2);
172
+ }
173
+
174
+ .medo-slider-field__dash {
175
+ color: var(--medo-color-text-secondary);
176
+ }
177
+
178
+ /* Tabular figures, so digits do not jump while the value changes. */
179
+ .medo-number-field__control {
180
+ font-variant-numeric: tabular-nums;
181
+ }
182
+
183
+ /* A click on the track or a key moves thumb and fill with a snappy spring; while dragging they follow 1:1. */
184
+ .medo-slider__fill {
185
+ transition:
186
+ inset-inline-start var(--medo-motion-spring-snappy-duration) var(--medo-motion-spring-snappy),
187
+ inline-size var(--medo-motion-spring-snappy-duration) var(--medo-motion-spring-snappy);
188
+ }
189
+ .medo-slider__track:has(.medo-slider__thumb[data-dragging]) :is(.medo-slider__thumb, .medo-slider__fill) {
190
+ transition-property: box-shadow;
191
+ }
192
+
193
+ @media (prefers-reduced-motion: reduce) {
194
+ .medo-slider__thumb {
195
+ transition: box-shadow var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
196
+ }
197
+ .medo-slider__fill {
198
+ transition: none;
199
+ }
200
+ }
201
+
202
+ @media (forced-colors: active) {
203
+ .medo-slider__fill,
204
+ .medo-slider__thumb {
205
+ forced-color-adjust: none;
206
+ background-color: Highlight;
207
+ border-color: CanvasText;
208
+ }
209
+ .medo-slider__rail {
210
+ background-color: GrayText;
211
+ }
212
+ }
package/src/styles.css ADDED
@@ -0,0 +1,48 @@
1
+ /*
2
+ * All component styles of @med-o/ui. Needs the tokens: @import "@med-o/design-tokens/css";
3
+ *
4
+ * Cascade layers: every component style sits in the layer `components`. CSS without a layer (your app) always
5
+ * wins over it, whatever the specificity – no !important needed. With Tailwind v4 the utilities win as well.
6
+ * The first line fixes the order of the layers (the same as Tailwind v4), so it holds in any import order.
7
+ */
8
+ @layer theme, base, components, utilities;
9
+
10
+ @import './utils/utilities.css' layer(components);
11
+ @import './button/button.css' layer(components);
12
+ @import './field/field.css' layer(components);
13
+ @import './text-field/input.css' layer(components);
14
+ @import './checkbox/choice.css' layer(components);
15
+ @import './switch/switch.css' layer(components);
16
+ @import './badge/badge.css' layer(components);
17
+ @import './tag/tag.css' layer(components);
18
+ @import './avatar/avatar.css' layer(components);
19
+ @import './tooltip/tooltip.css' layer(components);
20
+ @import './progress/progress.css' layer(components);
21
+ @import './list/list.css' layer(components);
22
+ @import './list-view/list-view.css' layer(components);
23
+ @import './bulk-action-bar/bulk-action-bar.css' layer(components);
24
+ @import './empty-state/empty-state.css' layer(components);
25
+ @import './pagination/pagination.css' layer(components);
26
+ @import './search/search.css' layer(components);
27
+ @import './breadcrumbs/breadcrumbs.css' layer(components);
28
+ @import './accordion/accordion.css' layer(components);
29
+ @import './slider/slider.css' layer(components);
30
+ @import './toggle-button/toggle-button.css' layer(components);
31
+ @import './listbox/listbox.css' layer(components);
32
+ @import './select/select.css' layer(components);
33
+ @import './combobox/combobox.css' layer(components);
34
+ @import './bottom-sheet/bottom-sheet.css' layer(components);
35
+ @import './menu/menu.css' layer(components);
36
+ @import './dialog/dialog.css' layer(components);
37
+ @import './drawer/drawer.css' layer(components);
38
+ @import './link/link.css' layer(components);
39
+ @import './alert/alert.css' layer(components);
40
+ @import './toast/toast.css' layer(components);
41
+ @import './tabs/tabs.css' layer(components);
42
+ @import './popover/popover.css' layer(components);
43
+ @import './navigation/navigation.css' layer(components);
44
+ @import './card/card.css' layer(components);
45
+ @import './table/table.css' layer(components);
46
+ @import './upload/upload.css' layer(components);
47
+ @import './date-time/date-time.css' layer(components);
48
+ @import './media/media.css' layer(components);
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Switch – med.o Design System
3
+ * On / off that takes effect immediately (no submit). The label never changes with the state.
4
+ *
5
+ * State contract: [data-selected] [data-hovered] [data-focus-visible] [data-disabled] [data-readonly]
6
+ * Label position: start (settings lists, switch at the end) or end (forms, switch first).
7
+ */
8
+
9
+ .medo-switch {
10
+ --_track-w: calc(var(--medo-space-8) + var(--medo-space-1));
11
+ --_track-h: var(--medo-space-5);
12
+ --_thumb: var(--medo-space-4);
13
+ --_pad: var(--medo-space-0-5);
14
+ --_track: var(--medo-color-action-toggle-off);
15
+ --_gap: var(--medo-space-2);
16
+
17
+ display: flex;
18
+ flex-direction: column;
19
+ font: var(--medo-typography-body-sm);
20
+ color: var(--medo-color-text-primary);
21
+ }
22
+
23
+ .medo-switch--lg {
24
+ --_track-w: calc(var(--medo-space-10) + var(--medo-space-1));
25
+ --_track-h: var(--medo-space-6);
26
+ --_thumb: var(--medo-space-5);
27
+ font: var(--medo-typography-body-md);
28
+ }
29
+
30
+ .medo-switch__button {
31
+ display: inline-flex;
32
+ align-items: flex-start;
33
+ gap: var(--_gap);
34
+ inline-size: fit-content;
35
+ cursor: pointer;
36
+ }
37
+
38
+ .medo-switch--label-start .medo-switch__button {
39
+ --_gap: var(--medo-space-4);
40
+ flex-direction: row-reverse;
41
+ justify-content: space-between;
42
+ inline-size: 100%;
43
+ }
44
+
45
+ .medo-switch__track {
46
+ box-sizing: border-box;
47
+ display: flex;
48
+ align-items: center;
49
+ flex-shrink: 0;
50
+ inline-size: var(--_track-w);
51
+ block-size: var(--_track-h);
52
+ padding: var(--_pad);
53
+ border-radius: var(--medo-radius-full);
54
+ background-color: var(--_track);
55
+ transition: background-color var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
56
+ }
57
+
58
+ .medo-switch__thumb {
59
+ display: grid;
60
+ place-items: center;
61
+ inline-size: var(--_thumb);
62
+ block-size: var(--_thumb);
63
+ border-radius: var(--medo-radius-full);
64
+ background-color: var(--medo-color-text-on-action);
65
+ color: var(--_track);
66
+ /* The thumb moves with a snappy spring. */
67
+ transition:
68
+ transform var(--medo-motion-spring-snappy-duration) var(--medo-motion-spring-snappy),
69
+ color var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
70
+ }
71
+
72
+ .medo-switch__thumb > svg {
73
+ inline-size: 70%;
74
+ block-size: 70%;
75
+ stroke-width: 3;
76
+ }
77
+
78
+ .medo-switch__description {
79
+ font: var(--medo-typography-body-sm);
80
+ color: var(--medo-color-text-secondary);
81
+ }
82
+
83
+ .medo-switch--label-end .medo-switch__description {
84
+ margin-inline-start: calc(var(--_track-w) + var(--_gap));
85
+ }
86
+
87
+ /* ---------- States ---------- */
88
+
89
+ .medo-switch__button[data-hovered] {
90
+ --_track: var(--medo-color-action-toggle-off-hover);
91
+ }
92
+
93
+ .medo-switch[data-selected] {
94
+ --_track: var(--medo-color-action-primary);
95
+ }
96
+
97
+ .medo-switch[data-selected] .medo-switch__button[data-hovered] {
98
+ --_track: var(--medo-color-action-primary-hover);
99
+ }
100
+
101
+ .medo-switch[data-selected] .medo-switch__thumb {
102
+ transform: translateX(calc(var(--_track-w) - var(--_thumb) - 2 * var(--_pad)));
103
+ }
104
+
105
+ .medo-switch[data-selected]:dir(rtl) .medo-switch__thumb {
106
+ transform: translateX(calc(-1 * (var(--_track-w) - var(--_thumb) - 2 * var(--_pad))));
107
+ }
108
+
109
+ .medo-switch__button[data-focus-visible] .medo-switch__track {
110
+ box-shadow: var(--medo-shadow-focus-ring);
111
+ }
112
+
113
+ .medo-switch[data-disabled] {
114
+ --_track: var(--medo-color-border-default);
115
+ color: var(--medo-color-text-disabled);
116
+ }
117
+
118
+ .medo-switch[data-disabled] .medo-switch__button {
119
+ cursor: not-allowed;
120
+ }
121
+
122
+ .medo-switch[data-disabled] .medo-switch__thumb {
123
+ background-color: var(--medo-color-surface-default);
124
+ color: var(--medo-color-text-disabled);
125
+ }
126
+
127
+ .medo-switch[aria-busy='true'] .medo-switch__button {
128
+ cursor: progress;
129
+ }
130
+
131
+ .medo-switch__spinner {
132
+ animation: medo-switch-spin 0.8s linear infinite;
133
+ }
134
+
135
+ @keyframes medo-switch-spin {
136
+ to {
137
+ transform: rotate(1turn);
138
+ }
139
+ }
140
+
141
+ @media (forced-colors: active) {
142
+ .medo-switch__track {
143
+ border: var(--medo-border-width-thin) solid CanvasText;
144
+ }
145
+ .medo-switch[data-selected] .medo-switch__track {
146
+ background-color: Highlight;
147
+ }
148
+ }
149
+
150
+ @media (prefers-reduced-motion: reduce) {
151
+ /* Reduced motion: a short, plain move – the state change stays visible. */
152
+ .medo-switch__thumb {
153
+ transition:
154
+ transform var(--medo-motion-duration-fast) var(--medo-motion-easing-standard),
155
+ color var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
156
+ }
157
+ .medo-switch__spinner {
158
+ animation-duration: 3s;
159
+ }
160
+ }
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Table – med.o Design System
3
+ *
4
+ * One look for two kinds of tables:
5
+ * Static (content, docs, simple data): a native <table> – read like a table, not navigable cell by cell.
6
+ * <div class="medo-table-container"><table class="medo-table">…</table></div>
7
+ * Interactive (sort, select): React Aria Table (role grid) with the same classes.
8
+ *
9
+ * Text left, numbers right with tabular figures (class medo-table__cell--number or align="end").
10
+ * Density: standard (rows 48, header 40) · compact (rows 40, header 36).
11
+ *
12
+ * State contract (interactive rows): [data-hovered] [data-pressed] [data-selected] [data-focus-visible] [data-disabled]
13
+ * Sortable header: [data-allows-sorting] [data-sort-direction] (aria-sort is set too).
14
+ */
15
+
16
+ .medo-table-container {
17
+ box-sizing: border-box;
18
+ inline-size: 100%;
19
+ overflow: auto;
20
+ border: var(--medo-border-width-thin) solid var(--medo-color-border-default);
21
+ border-radius: var(--medo-radius-lg);
22
+ background-color: var(--medo-color-surface-default);
23
+ }
24
+
25
+ /* Without the frame, e.g. inside a card or a documentation page. */
26
+ /* Scrolls sideways: the container takes the focus (see StaticTable), so it needs a visible ring. */
27
+ .medo-table-container:focus-visible {
28
+ outline: none;
29
+ box-shadow: var(--medo-shadow-focus-ring);
30
+ }
31
+
32
+ .medo-table-container--plain {
33
+ border: 0;
34
+ border-radius: 0;
35
+ background-color: transparent;
36
+ }
37
+
38
+ .medo-table {
39
+ --_row: var(--medo-space-12);
40
+ --_head: var(--medo-space-10);
41
+ --_pad: var(--medo-space-4);
42
+
43
+ inline-size: 100%;
44
+ border-collapse: separate;
45
+ border-spacing: 0;
46
+ font: var(--medo-typography-body-sm);
47
+ color: var(--medo-color-text-primary);
48
+ text-align: start;
49
+ }
50
+
51
+ .medo-table--compact {
52
+ --_row: var(--medo-space-10);
53
+ --_head: calc(var(--medo-space-8) + var(--medo-space-1)); /* 36 */
54
+ --_pad: var(--medo-space-3);
55
+ }
56
+
57
+ .medo-table caption {
58
+ padding: var(--medo-space-3) var(--_pad);
59
+ font: var(--medo-typography-label-md);
60
+ text-align: start;
61
+ }
62
+
63
+ /* ---------- Header ---------- */
64
+
65
+ .medo-table__column,
66
+ .medo-table thead th {
67
+ position: sticky;
68
+ inset-block-start: 0;
69
+ z-index: 1;
70
+ block-size: var(--_head);
71
+ padding: 0 var(--_pad);
72
+ border-block-end: var(--medo-border-width-thin) solid var(--medo-color-border-default);
73
+ background-color: var(--medo-color-surface-subtle);
74
+ font: var(--medo-typography-label-sm);
75
+ color: var(--medo-color-text-secondary);
76
+ text-align: start;
77
+ vertical-align: middle;
78
+ white-space: nowrap;
79
+ outline: none;
80
+ }
81
+
82
+ .medo-table__column[data-allows-sorting] {
83
+ cursor: pointer;
84
+ user-select: none;
85
+ }
86
+
87
+ .medo-table__column {
88
+ transition: color var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
89
+ }
90
+ .medo-table__column[data-hovered] {
91
+ color: var(--medo-color-text-primary);
92
+ }
93
+
94
+ .medo-table__column[data-focus-visible] {
95
+ box-shadow: var(--medo-shadow-focus-ring-inset);
96
+ }
97
+
98
+ .medo-table__column[aria-sort='ascending'],
99
+ .medo-table__column[aria-sort='descending'] {
100
+ font: var(--medo-typography-label-sm-strong);
101
+ color: var(--medo-color-text-primary);
102
+ }
103
+
104
+ .medo-table__column-inner {
105
+ display: inline-flex;
106
+ vertical-align: middle;
107
+ align-items: center;
108
+ gap: var(--medo-space-1);
109
+ }
110
+
111
+ .medo-table__sort {
112
+ inline-size: calc(var(--medo-space-3) + var(--medo-space-0-5)); /* 14 */
113
+ block-size: calc(var(--medo-space-3) + var(--medo-space-0-5));
114
+ stroke-width: 2.5;
115
+ }
116
+
117
+ /* Unsorted sortable columns show the arrow on hover and focus only. */
118
+ .medo-table__sort {
119
+ transition: opacity var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
120
+ }
121
+ .medo-table__column:not([aria-sort='ascending'], [aria-sort='descending']) .medo-table__sort {
122
+ opacity: 0;
123
+ }
124
+ .medo-table__column:is([data-hovered], [data-focus-visible]) .medo-table__sort {
125
+ opacity: 1;
126
+ }
127
+
128
+ /* ---------- Rows and cells ---------- */
129
+
130
+ .medo-table__row,
131
+ .medo-table tbody tr {
132
+ outline: none;
133
+ }
134
+
135
+ .medo-table__cell,
136
+ .medo-table tbody :is(td, th) {
137
+ block-size: var(--_row);
138
+ padding: var(--medo-space-2) var(--_pad);
139
+ border-block-end: var(--medo-border-width-thin) solid var(--medo-color-border-default);
140
+ vertical-align: middle;
141
+ text-align: start;
142
+ outline: none;
143
+ }
144
+
145
+ .medo-table tbody th {
146
+ font-weight: var(--medo-font-weight-medium);
147
+ }
148
+
149
+ .medo-table tbody tr:last-child > *,
150
+ .medo-table__row:last-child > .medo-table__cell {
151
+ border-block-end: 0;
152
+ }
153
+
154
+ /* Numbers: right, tabular, never truncated. */
155
+ .medo-table :is(th, td, .medo-table__column, .medo-table__cell):is(.medo-table__cell--number, [data-align='end']) {
156
+ text-align: end;
157
+ font-variant-numeric: tabular-nums;
158
+ white-space: nowrap;
159
+ }
160
+
161
+ .medo-table__column[data-align='end'] .medo-table__column-inner {
162
+ flex-direction: row-reverse;
163
+ }
164
+
165
+ /* Skeletons in number columns sit on the right, like the numbers will. */
166
+ .medo-table :is(.medo-table__cell--number, [data-align='end']) .medo-skeleton {
167
+ margin-inline-start: auto;
168
+ }
169
+
170
+ /* Utility columns (checkbox, actions, chevron) */
171
+ .medo-table :is(.medo-table__cell--utility, .medo-table__column--utility) {
172
+ inline-size: var(--medo-space-12);
173
+ padding-inline: var(--medo-space-4) 0;
174
+ }
175
+
176
+ /* Row feedback fades; pressed shows at once, so a tap feels immediate. */
177
+ .medo-table__row > .medo-table__cell {
178
+ transition: background-color var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
179
+ }
180
+ .medo-table__row[data-pressed] > .medo-table__cell {
181
+ transition-duration: 0s;
182
+ }
183
+
184
+ .medo-table__row[data-hovered] > .medo-table__cell {
185
+ background-color: var(--medo-color-state-hover);
186
+ }
187
+
188
+ .medo-table__row[data-pressed] > .medo-table__cell {
189
+ background-color: var(--medo-color-state-pressed);
190
+ }
191
+
192
+ .medo-table__row[data-selected] > .medo-table__cell {
193
+ background-color: var(--medo-color-action-selected);
194
+ }
195
+
196
+ .medo-table__row[data-selected][data-hovered] > .medo-table__cell {
197
+ background-color: var(--medo-color-action-selected-hover);
198
+ }
199
+
200
+ .medo-table__row[data-focus-visible] > .medo-table__cell:first-child {
201
+ box-shadow: inset var(--medo-focus-ring-width) 0 0 var(--medo-color-focus-ring);
202
+ }
203
+
204
+ .medo-table__row[data-focus-visible] {
205
+ outline: var(--medo-focus-ring-width) solid var(--medo-color-focus-ring);
206
+ outline-offset: calc(-1 * var(--medo-focus-ring-width));
207
+ }
208
+
209
+ .medo-table__cell[data-focus-visible] {
210
+ box-shadow: var(--medo-shadow-focus-ring-inset);
211
+ }
212
+
213
+ .medo-table__row[data-disabled] {
214
+ color: var(--medo-color-text-disabled);
215
+ }
216
+
217
+ .medo-table__row[data-href],
218
+ .medo-table__row[data-selection-mode] {
219
+ cursor: pointer;
220
+ }
221
+
222
+ /* Optional zebra stripes for wide, dense data. */
223
+ .medo-table--striped tbody tr:nth-child(even) > *,
224
+ .medo-table--striped .medo-table__row:nth-child(even) > .medo-table__cell {
225
+ background-color: var(--medo-color-surface-subtle);
226
+ }
227
+
228
+ /* Empty and loading states fill the body; toolbar and header stay. */
229
+ .medo-table__empty {
230
+ padding: var(--medo-space-12) var(--_pad);
231
+ text-align: center;
232
+ color: var(--medo-color-text-secondary);
233
+ }
234
+
235
+ @media (forced-colors: active) {
236
+ .medo-table__row[data-selected] > .medo-table__cell {
237
+ forced-color-adjust: none;
238
+ background-color: Highlight;
239
+ color: HighlightText;
240
+ }
241
+ .medo-table__row[data-focus-visible] {
242
+ outline-color: Highlight;
243
+ }
244
+ .medo-table-container:focus-visible {
245
+ outline: 2px solid Highlight;
246
+ outline-offset: 2px;
247
+ }
248
+ }
249
+
250
+ /* ---------- Frame: toolbar – table – footer in one bordered surface ---------- */
251
+
252
+ .medo-table-frame {
253
+ box-sizing: border-box;
254
+ display: flex;
255
+ flex-direction: column;
256
+ inline-size: 100%;
257
+ overflow: hidden;
258
+ border: var(--medo-border-width-thin) solid var(--medo-color-border-default);
259
+ border-radius: var(--medo-radius-lg);
260
+ background-color: var(--medo-color-surface-default);
261
+ }
262
+
263
+ .medo-table-frame--plain {
264
+ border: 0;
265
+ border-radius: 0;
266
+ background-color: transparent;
267
+ }
268
+
269
+ /* Search, filter, columns – spacer – primary action. Wraps on narrow screens. */
270
+ .medo-table__toolbar {
271
+ display: flex;
272
+ flex-wrap: wrap;
273
+ align-items: center;
274
+ gap: var(--medo-space-2);
275
+ padding: var(--medo-space-3) var(--medo-space-4);
276
+ }
277
+
278
+ .medo-table__footer {
279
+ padding: var(--medo-space-3) var(--medo-space-4);
280
+ border-block-start: var(--medo-border-width-thin) solid var(--medo-color-border-default);
281
+ }
282
+
283
+ /* Rows above the footer keep their bottom divider only between rows. */
284
+ .medo-table-frame .medo-table__row:last-child > .medo-table__cell {
285
+ border-block-end: 0;
286
+ }