@med-o/ui 0.1.0 → 0.4.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.
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Layout – med.o Design System
3
+ *
4
+ * Spacing rules: every gap between elements comes from one of six rules (tokens layout/gap/*, they grow with
5
+ * the layout mode mobile · tablet · desktop):
6
+ * inline side by side in one group (buttons, tags, filters) 8
7
+ * content inside one block (heading, text, actions) 16
8
+ * fields form fields below each other 16 · 24 · 24
9
+ * items cards and tiles in a grid or list (= layout gutter) 16 · 24 · 24
10
+ * group groups inside a section (sub-sections, fieldsets) 32 · 40 · 48
11
+ * section sections of a page (= layout section) 48 · 64 · 96
12
+ *
13
+ * <div class="medo-stack">…</div> vertical, gap content
14
+ * <div class="medo-stack medo-stack--horizontal">…</div> horizontal, gap inline
15
+ * <div class="medo-grid">…</div> as many columns as fit, gap items
16
+ * <div class="medo-grid medo-grid--layout"><div class="medo-grid__item" style="--medo-grid-span: 6">…</div></div>
17
+ * <div class="medo-container medo-container--lg">…</div>
18
+ * Another rule: add medo-gap-{rule}, e.g. medo-gap-section. It works on any flex or grid container.
19
+ */
20
+
21
+ /* ---------- Stack ---------- */
22
+
23
+ .medo-stack {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: var(--medo-layout-gap-content);
27
+ min-inline-size: 0;
28
+ }
29
+
30
+ .medo-stack--horizontal {
31
+ flex-direction: row;
32
+ gap: var(--medo-layout-gap-inline);
33
+ }
34
+
35
+ .medo-stack--wrap {
36
+ flex-wrap: wrap;
37
+ }
38
+
39
+ .medo-stack--align-start {
40
+ align-items: flex-start;
41
+ }
42
+ .medo-stack--align-center {
43
+ align-items: center;
44
+ }
45
+ .medo-stack--align-end {
46
+ align-items: flex-end;
47
+ }
48
+ .medo-stack--align-stretch {
49
+ align-items: stretch;
50
+ }
51
+ .medo-stack--align-baseline {
52
+ align-items: baseline;
53
+ }
54
+
55
+ .medo-stack--justify-start {
56
+ justify-content: flex-start;
57
+ }
58
+ .medo-stack--justify-center {
59
+ justify-content: center;
60
+ }
61
+ .medo-stack--justify-end {
62
+ justify-content: flex-end;
63
+ }
64
+ .medo-stack--justify-between {
65
+ justify-content: space-between;
66
+ }
67
+
68
+ /* ---------- Grid ---------- */
69
+
70
+ /* Default: as many columns as fit, each at least --medo-grid-min wide. auto-fill keeps empty tracks, so one or
71
+ two cards keep the size they have in a full row. min(…, 100%) stops a single column from overflowing. */
72
+ .medo-grid {
73
+ --medo-grid-min: 16rem;
74
+ display: grid;
75
+ grid-template-columns: repeat(auto-fill, minmax(min(var(--medo-grid-min), 100%), 1fr));
76
+ gap: var(--medo-layout-gap-items);
77
+ min-inline-size: 0;
78
+ }
79
+
80
+ /* A fixed number of equal columns. */
81
+ .medo-grid--columns {
82
+ grid-template-columns: repeat(var(--medo-grid-columns, 2), minmax(0, 1fr));
83
+ }
84
+
85
+ /* The layout grid: 4 · 8 · 12 columns with the gutter of the layout mode. */
86
+ .medo-grid--layout {
87
+ grid-template-columns: repeat(var(--medo-layout-columns), minmax(0, 1fr));
88
+ }
89
+
90
+ /* A span never exceeds the columns there are: span 6 is full width on a phone with 4 columns. */
91
+ .medo-grid__item {
92
+ grid-column: span var(--medo-grid-span, 1);
93
+ grid-column: span min(var(--medo-grid-span, 1), var(--medo-layout-columns));
94
+ min-inline-size: 0;
95
+ }
96
+
97
+ .medo-grid--columns > .medo-grid__item {
98
+ grid-column: span min(var(--medo-grid-span, 1), var(--medo-grid-columns, 2));
99
+ }
100
+
101
+ .medo-grid__item.medo-grid__item--full {
102
+ grid-column: 1 / -1;
103
+ }
104
+
105
+ /* ---------- Container ---------- */
106
+
107
+ /* Centred content with the page margin of the layout mode. The width limit applies to the content, the margin
108
+ comes on top (content-box), so text lines keep their measure on every screen. */
109
+ .medo-container {
110
+ box-sizing: content-box;
111
+ max-inline-size: var(--medo-container-xl);
112
+ margin-inline: auto;
113
+ padding-inline: var(--medo-layout-margin);
114
+ }
115
+
116
+ .medo-container--prose {
117
+ max-inline-size: var(--medo-container-prose);
118
+ }
119
+ .medo-container--md {
120
+ max-inline-size: var(--medo-container-md);
121
+ }
122
+ .medo-container--lg {
123
+ max-inline-size: var(--medo-container-lg);
124
+ }
125
+ .medo-container--full {
126
+ max-inline-size: none;
127
+ }
128
+
129
+ /* ---------- Spacing rules as classes (last, so they win over the defaults above) ---------- */
130
+
131
+ .medo-gap-inline {
132
+ gap: var(--medo-layout-gap-inline);
133
+ }
134
+ .medo-gap-content {
135
+ gap: var(--medo-layout-gap-content);
136
+ }
137
+ .medo-gap-fields {
138
+ gap: var(--medo-layout-gap-fields);
139
+ }
140
+ .medo-gap-items {
141
+ gap: var(--medo-layout-gap-items);
142
+ }
143
+ .medo-gap-group {
144
+ gap: var(--medo-layout-gap-group);
145
+ }
146
+ .medo-gap-section {
147
+ gap: var(--medo-layout-gap-section);
148
+ }
package/src/list/list.css CHANGED
@@ -7,7 +7,7 @@
7
7
  * </ul>
8
8
  * <dl class="medo-dl medo-dl--auto"><div class="medo-dl__row"><dt class="medo-dl__term"/><dd class="medo-dl__value"/></div></dl>
9
9
  * Without a size class the list takes the size of the surrounding text; markers scale with it.
10
- * Marker column 24 (numbers 28), aligned to the first line. Item spacing space/2. Level 2: ring, „a.“.
10
+ * Marker column 24 (numbers 28), aligned to the first line. Item spacing space/2. Level 2: ring, “a.”.
11
11
  */
12
12
 
13
13
  .medo-list {
@@ -79,7 +79,7 @@
79
79
  background-color: transparent;
80
80
  }
81
81
 
82
- /* Number: same style as the text, tabular figures, right aligned so „9.“ and „10.“ line up. */
82
+ /* Number: same style as the text, tabular figures, right aligned so “9.” and “10.” line up. */
83
83
  .medo-list--number > .medo-list__item {
84
84
  counter-increment: medo-list;
85
85
  }
@@ -58,7 +58,7 @@
58
58
  cursor: pointer;
59
59
  }
60
60
 
61
- /* Row feedback fades; pressed shows at once (iOS highlight). */
61
+ /* Row feedback fades; pressed shows at once, so a tap feels immediate. */
62
62
  .medo-list-row {
63
63
  transition: background-color var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
64
64
  }
@@ -35,7 +35,7 @@
35
35
  overflow: hidden;
36
36
  }
37
37
 
38
- /* Motion (iOS): unfolds from the field with a snappy spring and fades in; out: a quick fade. */
38
+ /* Motion: unfolds from the field with a snappy spring and fades in; out: a quick fade. */
39
39
  .medo-listbox-popover {
40
40
  transform-origin: var(--trigger-anchor-point, top);
41
41
  transition:
package/src/menu/menu.css CHANGED
@@ -32,7 +32,7 @@
32
32
  overflow: auto;
33
33
  }
34
34
 
35
- /* Motion (iOS): grows from the trigger with a snappy spring and fades in; out: a quick fade. Transitions, so a fast open–close reverses smoothly. */
35
+ /* Motion: grows from the trigger with a snappy spring and fades in; out: a quick fade. Transitions, so a fast open–close reverses smoothly. */
36
36
  .medo-menu-popover {
37
37
  transform-origin: var(--trigger-anchor-point, top);
38
38
  transition:
@@ -196,7 +196,6 @@
196
196
  padding: var(--medo-space-2) var(--medo-space-2) var(--medo-space-1);
197
197
  font: var(--medo-typography-overline);
198
198
  color: var(--medo-color-text-secondary);
199
- text-transform: uppercase;
200
199
  }
201
200
 
202
201
  .medo-nav-section__divider {
@@ -42,7 +42,7 @@
42
42
  inline-size: auto;
43
43
  }
44
44
 
45
- /* Motion (iOS): grows from the trigger with a snappy spring and fades in; out: a quick fade. Transitions, so a fast open–close reverses smoothly. */
45
+ /* Motion: grows from the trigger with a snappy spring and fades in; out: a quick fade. Transitions, so a fast open–close reverses smoothly. */
46
46
  .medo-popover {
47
47
  transform-origin: var(--trigger-anchor-point, top);
48
48
  transition:
@@ -115,7 +115,7 @@
115
115
 
116
116
  /* ---------- Mobile: native picker (single) ---------- */
117
117
 
118
- /* Invisible native select over the field: taps open the system picker. 16 px prevents iOS zoom. */
118
+ /* Invisible native select over the field: taps open the system picker. 16 px prevents the automatic zoom of mobile browsers. */
119
119
  .medo-select__native {
120
120
  position: absolute;
121
121
  inset: 0;
package/src/styles.css CHANGED
@@ -1,40 +1,49 @@
1
- /* All component styles of @med-o/ui. Needs the tokens: @import "@med-o/design-tokens/css"; */
2
- @import './utils/utilities.css';
3
- @import './button/button.css';
4
- @import './field/field.css';
5
- @import './text-field/input.css';
6
- @import './checkbox/choice.css';
7
- @import './switch/switch.css';
8
- @import './badge/badge.css';
9
- @import './tag/tag.css';
10
- @import './avatar/avatar.css';
11
- @import './tooltip/tooltip.css';
12
- @import './progress/progress.css';
13
- @import './list/list.css';
14
- @import './list-view/list-view.css';
15
- @import './bulk-action-bar/bulk-action-bar.css';
16
- @import './empty-state/empty-state.css';
17
- @import './pagination/pagination.css';
18
- @import './search/search.css';
19
- @import './breadcrumbs/breadcrumbs.css';
20
- @import './accordion/accordion.css';
21
- @import './slider/slider.css';
22
- @import './toggle-button/toggle-button.css';
23
- @import './listbox/listbox.css';
24
- @import './select/select.css';
25
- @import './combobox/combobox.css';
26
- @import './bottom-sheet/bottom-sheet.css';
27
- @import './menu/menu.css';
28
- @import './dialog/dialog.css';
29
- @import './drawer/drawer.css';
30
- @import './link/link.css';
31
- @import './alert/alert.css';
32
- @import './toast/toast.css';
33
- @import './tabs/tabs.css';
34
- @import './popover/popover.css';
35
- @import './navigation/navigation.css';
36
- @import './card/card.css';
37
- @import './table/table.css';
38
- @import './upload/upload.css';
39
- @import './date-time/date-time.css';
40
- @import './media/media.css';
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 './layout/layout.css' layer(components);
12
+ @import './button/button.css' layer(components);
13
+ @import './field/field.css' layer(components);
14
+ @import './text-field/input.css' layer(components);
15
+ @import './checkbox/choice.css' layer(components);
16
+ @import './switch/switch.css' layer(components);
17
+ @import './badge/badge.css' layer(components);
18
+ @import './tag/tag.css' layer(components);
19
+ @import './avatar/avatar.css' layer(components);
20
+ @import './tooltip/tooltip.css' layer(components);
21
+ @import './progress/progress.css' layer(components);
22
+ @import './list/list.css' layer(components);
23
+ @import './list-view/list-view.css' layer(components);
24
+ @import './bulk-action-bar/bulk-action-bar.css' layer(components);
25
+ @import './empty-state/empty-state.css' layer(components);
26
+ @import './pagination/pagination.css' layer(components);
27
+ @import './search/search.css' layer(components);
28
+ @import './breadcrumbs/breadcrumbs.css' layer(components);
29
+ @import './accordion/accordion.css' layer(components);
30
+ @import './slider/slider.css' layer(components);
31
+ @import './toggle-button/toggle-button.css' layer(components);
32
+ @import './listbox/listbox.css' layer(components);
33
+ @import './select/select.css' layer(components);
34
+ @import './combobox/combobox.css' layer(components);
35
+ @import './bottom-sheet/bottom-sheet.css' layer(components);
36
+ @import './menu/menu.css' layer(components);
37
+ @import './dialog/dialog.css' layer(components);
38
+ @import './drawer/drawer.css' layer(components);
39
+ @import './link/link.css' layer(components);
40
+ @import './alert/alert.css' layer(components);
41
+ @import './toast/toast.css' layer(components);
42
+ @import './tabs/tabs.css' layer(components);
43
+ @import './popover/popover.css' layer(components);
44
+ @import './navigation/navigation.css' layer(components);
45
+ @import './card/card.css' layer(components);
46
+ @import './table/table.css' layer(components);
47
+ @import './upload/upload.css' layer(components);
48
+ @import './date-time/date-time.css' layer(components);
49
+ @import './media/media.css' layer(components);
@@ -63,7 +63,7 @@
63
63
  border-radius: var(--medo-radius-full);
64
64
  background-color: var(--medo-color-text-on-action);
65
65
  color: var(--_track);
66
- /* The thumb moves with a snappy spring (iOS). */
66
+ /* The thumb moves with a snappy spring. */
67
67
  transition:
68
68
  transform var(--medo-motion-spring-snappy-duration) var(--medo-motion-spring-snappy),
69
69
  color var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
@@ -23,6 +23,12 @@
23
23
  }
24
24
 
25
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
+
26
32
  .medo-table-container--plain {
27
33
  border: 0;
28
34
  border-radius: 0;
@@ -167,7 +173,7 @@
167
173
  padding-inline: var(--medo-space-4) 0;
168
174
  }
169
175
 
170
- /* Row feedback fades; pressed shows at once (iOS highlight). */
176
+ /* Row feedback fades; pressed shows at once, so a tap feels immediate. */
171
177
  .medo-table__row > .medo-table__cell {
172
178
  transition: background-color var(--medo-motion-duration-fast) var(--medo-motion-easing-standard);
173
179
  }
@@ -235,6 +241,10 @@
235
241
  .medo-table__row[data-focus-visible] {
236
242
  outline-color: Highlight;
237
243
  }
244
+ .medo-table-container:focus-visible {
245
+ outline: 2px solid Highlight;
246
+ outline-offset: 2px;
247
+ }
238
248
  }
239
249
 
240
250
  /* ---------- Frame: toolbar – table – footer in one bordered surface ---------- */
package/src/tabs/tabs.css CHANGED
@@ -139,7 +139,7 @@
139
139
  border-radius: inherit;
140
140
  background-color: var(--medo-color-surface-thumb);
141
141
  box-shadow: var(--medo-shadow-elevation-1);
142
- /* SelectionIndicator animates translate and size from the previous tab – a snappy spring like the iOS segmented control. */
142
+ /* SelectionIndicator animates translate and size from the previous tab – a snappy spring, so the eye follows the selection. */
143
143
  transition:
144
144
  translate var(--medo-motion-spring-snappy-duration) var(--medo-motion-spring-snappy),
145
145
  width var(--medo-motion-spring-snappy-duration) var(--medo-motion-spring-snappy);
package/src/tag/tag.css CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Compact pill for a value. Two roles, never mixed:
5
5
  * removable – a chosen value (multi-select): the x removes it
6
- * selectable – a filter: the whole tag toggles, a check marks „selected“
6
+ * selectable – a filter: the whole tag toggles, a check marks “selected”
7
7
  *
8
8
  * Structure:
9
9
  * <div class="medo-tag medo-tag--md [medo-tag--selectable]">
@@ -8,7 +8,7 @@
8
8
  * </div>
9
9
  * <textarea class="medo-textarea medo-textarea--md"></textarea>
10
10
  *
11
- * Sizes sm / md / lg = 32 / 40 / 48 px. Mobile and touch: always lg (16 px text prevents iOS zoom).
11
+ * Sizes sm / md / lg = 32 / 40 / 48 px. Mobile and touch: always lg (16 px text prevents the automatic zoom of mobile browsers).
12
12
  * Focus: the border turns focus/ring plus 1 px inside (flush, no outer band).
13
13
  */
14
14
 
@@ -129,7 +129,7 @@
129
129
  }
130
130
  }
131
131
 
132
- /* ---------- Motion (View Transitions, iOS): in slides up with a smooth spring, out fades and shrinks a little;
132
+ /* ---------- Motion (View Transitions): in slides up with a smooth spring, out fades and shrinks a little;
133
133
  * the other toasts glide to their new place with the same spring. ---------- */
134
134
 
135
135
  ::view-transition-group(.medo-toast) {
@@ -68,7 +68,7 @@
68
68
  border-radius: var(--medo-radius-sm);
69
69
  }
70
70
 
71
- /* The selected fill is an indicator that slides between the items with a snappy spring (iOS segmented control). */
71
+ /* The selected fill is an indicator that slides between the items with a snappy spring. */
72
72
  .medo-toggle-group--segmented .medo-toggle:is([data-selected], [aria-pressed='true']) {
73
73
  --_bg: transparent;
74
74
  }
@@ -30,7 +30,7 @@
30
30
  white-space: nowrap;
31
31
  }
32
32
 
33
- /* Motion (iOS): fades in and settles from 96 % at the trigger; out: a quick fade without movement. */
33
+ /* Motion: fades in and settles from 96 % at the trigger; out: a quick fade without movement. */
34
34
  .medo-tooltip {
35
35
  transform-origin: var(--trigger-anchor-point, top);
36
36
  transition:
@@ -344,7 +344,7 @@
344
344
  color: var(--medo-color-feedback-danger-text);
345
345
  }
346
346
 
347
- /* New files slide in softly (iOS list insert). */
347
+ /* New files slide in softly, so the eye finds them. */
348
348
  .medo-upload__item {
349
349
  transition:
350
350
  opacity var(--medo-motion-duration-normal) var(--medo-motion-easing-enter),