ionbase-ui 0.4.0 → 0.6.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.
@@ -1,31 +1,46 @@
1
1
  /*
2
2
  * Header
3
3
  *
4
- * Measured from Figma `Header` (70:22088). Three device variants:
4
+ * Measured from Figma `Header` (69:335). Four Device variants, two axes:
5
5
  *
6
- * Desktop 56 tall, padding 8 / 64, end-slot gap 12
7
- * Tablet 64 tall, padding 12 / 32, end-slot gap 8
8
- * Mobile 64 tall, padding 12 / 16, end-slot gap 8
6
+ * Desktop 56 tall, padding 8 / 64, end-slot gap 12
7
+ * Tablet 56 tall, padding 8 / 24, end-slot gap 8
8
+ * Mobile-Closed 64 tall, padding 12 / 16, toggle only
9
+ * Mobile-Open as Mobile-Closed, plus the dropped Menu-Container
9
10
  *
10
- * WHY THIS IS MEDIA QUERIES AND NOT A PROP
11
+ * WHY DEVICE IS A MEDIA QUERY AND OPEN IS A PROP
11
12
  *
12
- * Device is the one variant axis in the whole system that the browser already
13
- * knows the answer to. Every other component takes its variant from the caller
13
+ * Device is the one variant axis in the whole system the browser already knows
14
+ * the answer to. Every other component takes its variant from the caller
14
15
  * because the caller is the only one who knows; a header is Mobile because the
15
16
  * viewport is narrow, and asking React to duplicate that is how the two get out
16
- * of step. The breakpoints match the Breakpoint collection's own container
17
- * widths — Tablet below 1216, Mobile below 896.
17
+ * of step. Breakpoints match the Breakpoint collection's own container widths —
18
+ * Tablet below 1216, Mobile below 896.
18
19
  *
19
- * Note the header is taller on Tablet and Mobile than on Desktop (64 vs 56).
20
+ * Open/Closed is the opposite: nothing in CSS knows it, so it is a prop, and it
21
+ * arrives here as `[data-open]`.
22
+ *
23
+ * Note the header is taller on Mobile (64) than on Desktop and Tablet (56).
20
24
  * That looks inverted but is deliberate in the design: touch targets need the
21
25
  * height more than the desktop pointer does.
26
+ *
27
+ * ONE TREE, TWO LAYOUTS
28
+ *
29
+ * `__menu` is `display: contents` above the mobile breakpoint, so `__center`
30
+ * and `__end` are flex items of the bar itself; below it, the same element
31
+ * becomes the absolutely positioned Menu-Container. The slots are therefore
32
+ * rendered once, never duplicated across breakpoints.
22
33
  */
23
34
  .ion-header {
24
- --ion-header-height: var(--spacing-64);
25
- --ion-header-padding-y: var(--spacing-12);
26
- --ion-header-padding-x: var(--spacing-16);
27
- --ion-header-end-gap: var(--spacing-8);
35
+ /* Desktop is the base; the two narrower breakpoints override downward, so a
36
+ * new rung is added in one place rather than three. */
37
+ --ion-header-height: calc(var(--spacing-48) + var(--spacing-8)); /* 56 */
38
+ --ion-header-padding-y: var(--spacing-8);
39
+ --ion-header-padding-x: var(--spacing-64);
40
+ --ion-header-center-gap: var(--spacing-8);
41
+ --ion-header-end-gap: var(--spacing-12);
28
42
 
43
+ position: relative;
29
44
  display: flex;
30
45
  align-items: center;
31
46
  justify-content: space-between;
@@ -43,20 +58,18 @@
43
58
  font-family: var(--font-family-sans);
44
59
  }
45
60
 
46
- @media (width >= 896px) {
61
+ @media (width < 1216px) {
47
62
  .ion-header {
48
- --ion-header-padding-x: var(--spacing-32);
63
+ --ion-header-padding-x: var(--spacing-24);
64
+ --ion-header-end-gap: var(--spacing-8);
49
65
  }
50
66
  }
51
67
 
52
- @media (width >= 1216px) {
68
+ @media (width < 896px) {
53
69
  .ion-header {
54
- /* 56 is off the spacing scale (it jumps 48 -> 64), so it is stated as
55
- * arithmetic on two rungs rather than as a literal. */
56
- --ion-header-height: calc(var(--spacing-64) - var(--spacing-8));
57
- --ion-header-padding-y: var(--spacing-8);
58
- --ion-header-padding-x: var(--spacing-64);
59
- --ion-header-end-gap: var(--spacing-12);
70
+ --ion-header-height: var(--spacing-64);
71
+ --ion-header-padding-y: var(--spacing-12);
72
+ --ion-header-padding-x: var(--spacing-16);
60
73
  }
61
74
  }
62
75
 
@@ -69,27 +82,117 @@
69
82
  }
70
83
 
71
84
  /*
72
- * The centre slot is hidden on Tablet and Mobile in Figma — both variants ship
73
- * it collapsed to zero. It is display:none rather than visibility:hidden so it
74
- * does not hold width the narrow layouts do not have.
85
+ * Above the mobile breakpoint the wrapper contributes no box of its own — its
86
+ * two children are laid out by the bar. `display: contents` is what makes the
87
+ * single-tree approach possible; without it the slots would need to be either
88
+ * duplicated or moved by script.
75
89
  */
90
+ .ion-header__menu {
91
+ display: contents;
92
+ }
93
+
76
94
  .ion-header__center {
77
- display: none;
95
+ display: flex;
78
96
  align-items: center;
79
- gap: var(--spacing-8);
80
- flex: 1 1 auto;
97
+ gap: var(--ion-header-center-gap);
81
98
  min-width: 0;
82
99
  }
83
100
 
84
- @media (width >= 1216px) {
85
- .ion-header__center {
86
- display: flex;
87
- }
88
- }
89
-
90
101
  .ion-header__end {
91
102
  display: flex;
92
103
  align-items: center;
93
104
  gap: var(--ion-header-end-gap);
94
105
  flex-shrink: 0;
95
106
  }
107
+
108
+ /*
109
+ * The Mobile-Closed / Mobile-Open toggle. Figma builds it from the Icon Button
110
+ * component (Secondary, Small, Square) but the Header owns the instance, so the
111
+ * geometry is stated here rather than reached for across components: 32 square,
112
+ * radius/sm, 16px icon.
113
+ */
114
+ .ion-header__toggle {
115
+ display: none;
116
+ align-items: center;
117
+ justify-content: center;
118
+ flex-shrink: 0;
119
+ width: var(--spacing-32);
120
+ height: var(--spacing-32);
121
+ padding: 0;
122
+ color: var(--icon-default);
123
+ background-color: var(--surface-default);
124
+ border: var(--border-width-default) solid var(--border-strong);
125
+ border-radius: var(--radius-sm);
126
+ box-shadow: var(--ion-shadow-raised-lifted-xs);
127
+ cursor: pointer;
128
+ }
129
+
130
+ .ion-header__toggle > svg {
131
+ width: var(--icon-size-sm);
132
+ height: var(--icon-size-sm);
133
+ }
134
+
135
+ .ion-header__toggle:focus-visible {
136
+ outline: var(--border-width-thick) solid var(--ring-focus);
137
+ outline-offset: var(--spacing-2);
138
+ }
139
+
140
+ @media (width < 896px) {
141
+ .ion-header__toggle {
142
+ display: inline-flex;
143
+ }
144
+
145
+ /*
146
+ * Menu-Container. Figma places it at y=67 against a 64-tall bar — a 3px nudge
147
+ * that is on no scale and matches no token, so it is anchored flush to the
148
+ * bar's bottom edge instead. Nothing else in the design depends on the gap.
149
+ */
150
+ .ion-header__menu {
151
+ position: absolute;
152
+ top: 100%;
153
+ left: 0;
154
+ z-index: 1;
155
+ display: flex;
156
+ flex-direction: column;
157
+ gap: var(--spacing-12);
158
+ width: 100%;
159
+ padding: var(--spacing-12) var(--spacing-8);
160
+ background-color: var(--surface-default);
161
+ border: var(--border-width-default) solid var(--border-default);
162
+ border-radius: var(--radius-md);
163
+ }
164
+
165
+ .ion-header[data-open] .ion-header__menu {
166
+ display: flex;
167
+ }
168
+
169
+ .ion-header:not([data-open]) .ion-header__menu {
170
+ display: none;
171
+ }
172
+
173
+ /* Both slots stack full width inside the panel; the end slot keeps its row
174
+ * but shares the width evenly, as the two buttons do in Figma. */
175
+ .ion-header__center {
176
+ flex-direction: column;
177
+ align-items: stretch;
178
+ width: 100%;
179
+ }
180
+
181
+ .ion-header__end {
182
+ width: 100%;
183
+ }
184
+
185
+ .ion-header__end > * {
186
+ flex: 1 1 0;
187
+ }
188
+ }
189
+
190
+ /*
191
+ * Forced-colors: box-shadow is dropped by the OS, so the toggle would lose its
192
+ * only edge. A border in ButtonText keeps it visible.
193
+ */
194
+ @media (forced-colors: active) {
195
+ .ion-header__toggle {
196
+ border-color: ButtonText;
197
+ }
198
+ }
@@ -36,6 +36,7 @@
36
36
  @import url('./divider.css');
37
37
  @import url('./nav-item.css');
38
38
  @import url('./scroll-progress.css');
39
+ @import url('./full-card.css');
39
40
 
40
41
  /*
41
42
  * Every IonBase element is border-box.
@@ -108,6 +108,28 @@
108
108
  --spacing-80: 80px;
109
109
  --spacing-96: 96px;
110
110
  --spacing-128: 128px;
111
+ --scale-0: 0px;
112
+ --scale-1: 1px;
113
+ --scale-2: 2px;
114
+ --scale-4: 4px;
115
+ --scale-6: 6px;
116
+ --scale-8: 8px;
117
+ --scale-10: 10px;
118
+ --scale-12: 12px;
119
+ --scale-14: 14px;
120
+ --scale-16: 16px;
121
+ --scale-20: 20px;
122
+ --scale-24: 24px;
123
+ --scale-32: 32px;
124
+ --scale-full: 9999px;
125
+ --font-family-host-grotesk: "Host Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
126
+ --font-family-space-mono: "Space Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
127
+ --font-family-merriweather: Merriweather, Georgia, "Times New Roman", serif;
128
+ --font-family-stix-two-text: "STIX Two Text", Georgia, "Times New Roman", serif;
129
+ --font-family-sans: "Host Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
130
+ --font-family-serif: Merriweather, Georgia, "Times New Roman", serif;
131
+ --font-family-mono: "Space Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
132
+ --font-family-serif-display: "STIX Two Text", Georgia, "Times New Roman", serif;
111
133
  --font-size-12: 12px;
112
134
  --font-size-14: 14px;
113
135
  --font-size-16: 16px;
@@ -122,14 +144,6 @@
122
144
  --font-size-56: 56px;
123
145
  --font-size-64: 64px;
124
146
  --font-size-72: 72px;
125
- --font-family-host-grotesk: "Host Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
126
- --font-family-merriweather: Merriweather, Georgia, "Times New Roman", serif;
127
- --font-family-space-mono: "Space Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
128
- --font-family-stix-two-text: "STIX Two Text", Georgia, "Times New Roman", serif;
129
- --font-family-sans: "Host Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
130
- --font-family-serif: Merriweather, Georgia, "Times New Roman", serif;
131
- --font-family-mono: "Space Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
132
- --font-family-serif-display: "STIX Two Text", Georgia, "Times New Roman", serif;
133
147
  --font-weight-400: 400;
134
148
  --font-weight-500: 500;
135
149
  --font-weight-600: 600;
@@ -138,20 +152,6 @@
138
152
  --font-weight-medium: 500;
139
153
  --font-weight-semibold: 600;
140
154
  --font-weight-bold: 700;
141
- --scale-0: 0px;
142
- --scale-1: 1px;
143
- --scale-2: 2px;
144
- --scale-4: 4px;
145
- --scale-6: 6px;
146
- --scale-8: 8px;
147
- --scale-10: 10px;
148
- --scale-12: 12px;
149
- --scale-14: 14px;
150
- --scale-16: 16px;
151
- --scale-20: 20px;
152
- --scale-24: 24px;
153
- --scale-32: 32px;
154
- --scale-full: 9999px;
155
155
  --primary-50: #ebf7ff;
156
156
  --primary-100: #e2f1fd;
157
157
  --primary-200: #c7e4fa;
@@ -260,7 +260,7 @@
260
260
  --text-primary: #1c62e3;
261
261
  --text-error: #ba1c1c;
262
262
  --text-success: #0d7d38;
263
- --text-warning: #8e2f07;
263
+ --text-warning: #af3e0e;
264
264
  --text-information: #4715b2;
265
265
  --text-link: #1541b2;
266
266
  --text-link-hover: #0e2d8b;
@@ -271,6 +271,7 @@
271
271
  --icon-secondary: #384252;
272
272
  --icon-tertiary: #4b5563;
273
273
  --icon-disabled: #6b7280;
274
+ --icon-placeholder: #9ca3b0;
274
275
  --icon-on-color: #ffffff;
275
276
  --icon-inverse: #f6f8f9;
276
277
  --icon-primary: #1c62e3;
@@ -34,19 +34,19 @@
34
34
  0px 8px 8px -4px rgb(0 0 0 / 4%);
35
35
 
36
36
  --ion-shadow-raised-lifted-xs:
37
- inset 0px -2px 1px 0px rgb(0 0 0 / 25%),
37
+ inset 0px -2px 1px 0px rgb(0 0 0 / 12%),
38
38
  inset 0px 2px 1px 0px rgb(255 255 255 / 20%),
39
39
  0px 1px 1px 0px rgb(7 10 13 / 6%),
40
40
  0px 1px 2px 0px rgb(7 10 13 / 4%);
41
41
 
42
42
  --ion-shadow-raised-lifted-sm:
43
- inset 0px -3px 1px 0px rgb(0 0 0 / 25%),
43
+ inset 0px -3px 1px 0px rgb(0 0 0 / 15%),
44
44
  inset 0px 3px 1px 0px rgb(255 255 255 / 20%),
45
45
  0px 1px 2px 0px rgb(7 10 13 / 8%),
46
46
  0px 2px 4px 0px rgb(7 10 13 / 5%);
47
47
 
48
48
  --ion-shadow-raised-lifted-lg:
49
- inset 0px -4px 2px 0px rgb(0 0 0 / 25%),
49
+ inset 0px -4px 2px 0px rgb(0 0 0 / 20%),
50
50
  inset 0px 4px 2px 0px rgb(255 255 255 / 20%),
51
51
  0px 1px 1px 0px rgb(7 10 13 / 32%),
52
52
  0px 1px 2px 0px rgb(7 10 13 / 16%);
@@ -74,15 +74,15 @@
74
74
  0px 2px 6px 0px rgb(7 10 13 / 20%);
75
75
 
76
76
  --ion-shadow-raised-flush-xs:
77
- inset 0px -2px 1px 0px rgb(0 0 0 / 25%),
77
+ inset 0px -2px 1px 0px rgb(0 0 0 / 12%),
78
78
  inset 0px 2px 1px 0px rgb(255 255 255 / 20%);
79
79
 
80
80
  --ion-shadow-raised-flush-sm:
81
- inset 0px -3px 1px 0px rgb(0 0 0 / 25%),
81
+ inset 0px -3px 1px 0px rgb(0 0 0 / 15%),
82
82
  inset 0px 3px 1px 0px rgb(255 255 255 / 20%);
83
83
 
84
84
  --ion-shadow-raised-flush-lg:
85
- inset 0px -4px 2px 0px rgb(0 0 0 / 25%),
85
+ inset 0px -4px 2px 0px rgb(0 0 0 / 20%),
86
86
  inset 0px 4px 2px 0px rgb(255 255 255 / 20%);
87
87
 
88
88
  --ion-shadow-raised-flush-xl:
@@ -6,7 +6,7 @@
6
6
  --text-default: #f6f8f9;
7
7
  --text-secondary: #dde0e4;
8
8
  --text-tertiary: #9ca3b0;
9
- --text-placeholder: #6b7280;
9
+ --text-placeholder: #4b5563;
10
10
  --text-on-color: #000000;
11
11
  --text-inverse: #131923;
12
12
  --text-primary: #609cf0;
@@ -22,6 +22,7 @@
22
22
  --icon-default: #f0f2f4;
23
23
  --icon-secondary: #dde0e4;
24
24
  --icon-tertiary: #9ca3b0;
25
+ --icon-placeholder: #4b5563;
25
26
  --icon-inverse: #131923;
26
27
  --icon-primary: #609cf0;
27
28
  --icon-error: #f56b6b;
@@ -14,14 +14,14 @@
14
14
  }
15
15
 
16
16
  .ion-text-h1 {
17
- font-family: var(--font-family-serif-display);
17
+ font-family: var(--font-family-sans);
18
18
  font-weight: var(--font-weight-semibold);
19
19
  font-size: var(--type-h1);
20
20
  line-height: var(--type-h1-line-height);
21
21
  }
22
22
 
23
23
  .ion-text-h2 {
24
- font-family: var(--font-family-serif-display);
24
+ font-family: var(--font-family-sans);
25
25
  font-weight: var(--font-weight-semibold);
26
26
  font-size: var(--type-h2);
27
27
  line-height: var(--type-h2-line-height);
@@ -85,7 +85,7 @@
85
85
 
86
86
  .ion-text-caption {
87
87
  font-family: var(--font-family-sans);
88
- font-weight: var(--font-weight-medium);
88
+ font-weight: var(--font-weight-regular);
89
89
  font-size: var(--type-caption);
90
90
  line-height: var(--type-caption-line-height);
91
91
  }