@uncinq/css-components 1.16.6 → 1.18.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <img width="1280" height="640" alt="share-css-components" src="https://github.com/user-attachments/assets/3d74df8a-e7bf-4ba5-8529-99cfef0bc176" />
6
6
 
7
- 29 components and 1 utility, written as plain CSS, reading their values from [@uncinq/component-tokens](https://github.com/uncinq/component-tokens) and adding none of their own.
7
+ 30 components and 1 utility, written as plain CSS, reading their values from [@uncinq/component-tokens](https://github.com/uncinq/component-tokens) and adding none of their own.
8
8
 
9
9
  ## Where this sits
10
10
 
@@ -55,7 +55,7 @@ The failure is silent. No error is raised, the page simply stays at its mobile v
55
55
  | --- | --- |
56
56
  | Buttons | `.btn` with colour, size and style variants, plus `btn-close`, `btn-menu`, `btn-search`, `btn-filter`, `btn-share`, `btn-toc`, `btn-toggle-video` |
57
57
  | Overlays | `modal`, `drawer`, `dropdown`, and the shared panel skeleton |
58
- | Content | `alert`, `badge`, `banner`, `card`, `items`, `list`, `media`, `surtitle` |
58
+ | Content | `alert`, `badge`, `banner`, `card`, `items`, `list`, `media`, `push`, `surtitle` |
59
59
  | Navigation | `nav`, `nav-title`, `nav-accessibility`, `breadcrumb`, `pagination` |
60
60
  | Embeds | `embed`, `video`, `map` |
61
61
  | Forms | `form`, `form-check` and the layout helpers |
@@ -25,52 +25,52 @@
25
25
  /* Variants */
26
26
  .badge-brand {
27
27
  --badge-color-background: var(--color-brand);
28
- --badge-color-background-hover: var(--color-brand-strong);
28
+ --badge-color-background-hover: var(--color-brand-hover);
29
29
  --badge-color-text: var(--color-text-on-brand);
30
30
  }
31
31
  .badge-danger {
32
32
  --badge-color-background: var(--color-danger);
33
- --badge-color-background-hover: var(--color-danger-strong);
33
+ --badge-color-background-hover: var(--color-danger-hover);
34
34
  --badge-color-text: var(--color-text-on-danger);
35
35
  }
36
36
  .badge-dark {
37
37
  --badge-color-background: var(--color-dark);
38
- --badge-color-background-hover: var(--color-dark-strong);
38
+ --badge-color-background-hover: var(--color-dark-hover);
39
39
  --badge-color-text: var(--color-text-on-dark);
40
40
  }
41
41
  .badge-info {
42
42
  --badge-color-background: var(--color-info);
43
- --badge-color-background-hover: var(--color-info-strong);
43
+ --badge-color-background-hover: var(--color-info-hover);
44
44
  --badge-color-text: var(--color-text-on-info);
45
45
  }
46
46
  .badge-light {
47
47
  --badge-color-background: var(--color-light);
48
- --badge-color-background-hover: var(--color-light-strong);
48
+ --badge-color-background-hover: var(--color-light-hover);
49
49
  --badge-color-text: var(--color-text-on-light);
50
50
  }
51
51
  .badge-neutral {
52
52
  --badge-color-background: var(--color-neutral);
53
- --badge-color-background-hover: var(--color-neutral-strong);
53
+ --badge-color-background-hover: var(--color-neutral-hover);
54
54
  --badge-color-text: var(--color-text-on-neutral);
55
55
  }
56
56
  .badge-primary {
57
57
  --badge-color-background: var(--color-brand);
58
- --badge-color-background-hover: var(--color-brand-strong);
58
+ --badge-color-background-hover: var(--color-brand-hover);
59
59
  --badge-color-text: var(--color-text-on-brand);
60
60
  }
61
61
  .badge-secondary {
62
62
  --badge-color-background: var(--color-neutral);
63
- --badge-color-background-hover: var(--color-neutral-strong);
63
+ --badge-color-background-hover: var(--color-neutral-hover);
64
64
  --badge-color-text: var(--color-text-on-neutral);
65
65
  }
66
66
  .badge-success {
67
67
  --badge-color-background: var(--color-success);
68
- --badge-color-background-hover: var(--color-success-strong);
68
+ --badge-color-background-hover: var(--color-success-hover);
69
69
  --badge-color-text: var(--color-text-on-success);
70
70
  }
71
71
  .badge-warning {
72
72
  --badge-color-background: var(--color-warning);
73
- --badge-color-background-hover: var(--color-warning-strong);
73
+ --badge-color-background-hover: var(--color-warning-hover);
74
74
  --badge-color-text: var(--color-text-on-warning);
75
75
  }
76
76
  }
@@ -55,9 +55,9 @@
55
55
  /* Variants */
56
56
  .btn-brand {
57
57
  --btn-color-background: var(--color-brand);
58
- --btn-color-background-hover: var(--color-brand-strong);
58
+ --btn-color-background-hover: var(--color-brand-hover);
59
59
  --btn-color-border: var(--color-brand);
60
- --btn-color-border-hover: var(--color-brand-strong);
60
+ --btn-color-border-hover: var(--color-brand-hover);
61
61
  --btn-color-text: var(--color-text-on-brand);
62
62
  }
63
63
 
@@ -72,17 +72,17 @@
72
72
 
73
73
  .btn-danger {
74
74
  --btn-color-background: var(--color-danger);
75
- --btn-color-background-hover: var(--color-danger-strong);
75
+ --btn-color-background-hover: var(--color-danger-hover);
76
76
  --btn-color-border: var(--color-danger);
77
- --btn-color-border-hover: var(--color-danger-strong);
77
+ --btn-color-border-hover: var(--color-danger-hover);
78
78
  --btn-color-text: var(--color-text-on-danger);
79
79
  }
80
80
 
81
81
  .btn-dark {
82
82
  --btn-color-background: var(--color-dark);
83
- --btn-color-background-hover: var(--color-dark-strong);
83
+ --btn-color-background-hover: var(--color-dark-hover);
84
84
  --btn-color-border: var(--color-dark);
85
- --btn-color-border-hover: var(--color-dark-strong);
85
+ --btn-color-border-hover: var(--color-dark-hover);
86
86
  --btn-color-text: var(--color-text-on-dark);
87
87
  }
88
88
 
@@ -97,17 +97,17 @@
97
97
 
98
98
  .btn-info {
99
99
  --btn-color-background: var(--color-info);
100
- --btn-color-background-hover: var(--color-info-strong);
100
+ --btn-color-background-hover: var(--color-info-hover);
101
101
  --btn-color-border: var(--color-info);
102
- --btn-color-border-hover: var(--color-info-strong);
102
+ --btn-color-border-hover: var(--color-info-hover);
103
103
  --btn-color-text: var(--color-text-on-info);
104
104
  }
105
105
 
106
106
  .btn-light {
107
107
  --btn-color-background: var(--color-light);
108
- --btn-color-background-hover: var(--color-light-strong);
108
+ --btn-color-background-hover: var(--color-light-hover);
109
109
  --btn-color-border: var(--color-light);
110
- --btn-color-border-hover: var(--color-light-strong);
110
+ --btn-color-border-hover: var(--color-light-hover);
111
111
  --btn-color-text: var(--color-text-on-light);
112
112
  }
113
113
 
@@ -129,41 +129,41 @@
129
129
 
130
130
  .btn-neutral {
131
131
  --btn-color-background: var(--color-neutral);
132
- --btn-color-background-hover: var(--color-neutral-strong);
132
+ --btn-color-background-hover: var(--color-neutral-hover);
133
133
  --btn-color-border: var(--color-neutral);
134
- --btn-color-border-hover: var(--color-neutral-strong);
134
+ --btn-color-border-hover: var(--color-neutral-hover);
135
135
  --btn-color-text: var(--color-text-on-neutral);
136
136
  }
137
137
 
138
138
  .btn-primary {
139
139
  --btn-color-background: var(--color-brand);
140
- --btn-color-background-hover: var(--color-brand-strong);
140
+ --btn-color-background-hover: var(--color-brand-hover);
141
141
  --btn-color-border: var(--color-brand);
142
- --btn-color-border-hover: var(--color-brand-strong);
142
+ --btn-color-border-hover: var(--color-brand-hover);
143
143
  --btn-color-text: var(--color-text-on-brand);
144
144
  }
145
145
 
146
146
  .btn-secondary {
147
147
  --btn-color-background: var(--color-neutral);
148
- --btn-color-background-hover: var(--color-neutral-strong);
148
+ --btn-color-background-hover: var(--color-neutral-hover);
149
149
  --btn-color-border: var(--color-neutral);
150
- --btn-color-border-hover: var(--color-neutral-strong);
150
+ --btn-color-border-hover: var(--color-neutral-hover);
151
151
  --btn-color-text: var(--color-text-on-neutral);
152
152
  }
153
153
 
154
154
  .btn-success {
155
155
  --btn-color-background: var(--color-success);
156
- --btn-color-background-hover: var(--color-success-strong);
156
+ --btn-color-background-hover: var(--color-success-hover);
157
157
  --btn-color-border: var(--color-success);
158
- --btn-color-border-hover: var(--color-success-strong);
158
+ --btn-color-border-hover: var(--color-success-hover);
159
159
  --btn-color-text: var(--color-text-on-success);
160
160
  }
161
161
 
162
162
  .btn-warning {
163
163
  --btn-color-background: var(--color-warning);
164
- --btn-color-background-hover: var(--color-warning-strong);
164
+ --btn-color-background-hover: var(--color-warning-hover);
165
165
  --btn-color-border: var(--color-warning);
166
- --btn-color-border-hover: var(--color-warning-strong);
166
+ --btn-color-border-hover: var(--color-warning-hover);
167
167
  --btn-color-text: var(--color-text-on-warning);
168
168
  }
169
169
 
@@ -0,0 +1,256 @@
1
+ /* components/push.css */
2
+ /*
3
+ * .push — a video or an image with a surtitle, title, text and CTA laid over it.
4
+ * Reads --push-* from @uncinq/component-tokens.
5
+ *
6
+ * Structure, the same for every modifier:
7
+ * <div class="push">
8
+ * <div class="content">
9
+ * <p class="surtitle">...</p>
10
+ * <h2 class="title">...</h2>
11
+ * <div class="text">...</div>
12
+ * <a class="btn">...</a>
13
+ * </div>
14
+ * <div class="media">
15
+ * picture | .video > video
16
+ * </div>
17
+ * </div>
18
+ *
19
+ * The push carries the ratio and places .content inside it; .content is only
20
+ * the text box, which .push-card turns into a card. The ratio is drawn by
21
+ * .push::after, so that pseudo-element is taken.
22
+ *
23
+ * Modifiers:
24
+ * .push-center | .push-end horizontal position of the content
25
+ * .push-align-start | -center | -end text alignment
26
+ * .push-vertical-start | .push-vertical-end vertical position of the content
27
+ * .push-card background behind the text
28
+ * .push-darken gradient under the text
29
+ */
30
+ @layer components {
31
+ .push {
32
+ --media-color-background: var(--push-color-background);
33
+ --ratio-video: auto;
34
+
35
+ border-radius: var(--push-border-radius);
36
+ display: grid;
37
+ /* A fixed track: sized by its content, it would widen to the ratio box's
38
+ transferred size whenever the push is stretched by a min-height. */
39
+ grid-template-columns: minmax(0, 1fr);
40
+ overflow: hidden;
41
+ padding-block: var(--push-content-padding-block);
42
+ padding-inline: var(--push-content-padding-inline);
43
+ position: relative;
44
+
45
+ @media (--sm) {
46
+ --push-content-max-inline-size: var(--push-content-max-inline-size-tablet);
47
+ }
48
+ @media (--lg) {
49
+ --push-content-max-inline-size: var(--push-content-max-inline-size-laptop);
50
+ }
51
+
52
+ /* The ratio: an empty box sharing the single grid cell with .content, and
53
+ stretched over the padding by negative margins so that the ratio is the
54
+ push's own. The push takes the taller of the two, so a text that needs
55
+ more room grows it rather than being cut, even under a min-height. */
56
+ &::after {
57
+ aspect-ratio: var(--push-ratio-mobile);
58
+ content: '';
59
+ grid-area: 1 / 1;
60
+ justify-self: stretch;
61
+ margin-block: calc(var(--push-content-padding-block) * -1);
62
+ margin-inline: calc(var(--push-content-padding-inline) * -1);
63
+
64
+ @media (--sm) {
65
+ aspect-ratio: var(--push-ratio);
66
+ }
67
+ }
68
+
69
+ &:not(.push-card) {
70
+ --btn-color-background: var(--color-light);
71
+ --btn-color-background-hover: var(--color-light-hover);
72
+ --btn-color-border: var(--color-light);
73
+ --btn-color-border-hover: var(--color-light-hover);
74
+ --btn-color-text: var(--color-text-on-light);
75
+ --color-heading: var(--push-color);
76
+ --color-link: var(--push-color);
77
+ --color-link-hover: var(--push-color);
78
+ --surtitle-color-text: var(--push-surtitle-color, oklch(from var(--push-color) l c h / var(--opacity-muted)));
79
+
80
+ color: var(--push-color);
81
+ }
82
+
83
+ /* A grid item stacks above the media and the darken overlay through its
84
+ z-index alone. Left unpositioned, it is no containing block, so the
85
+ .btn::after overlay below covers the whole push, not just the text. */
86
+ > .content {
87
+ align-self: var(--push-content-justify, center);
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: var(--push-content-gap);
91
+ grid-area: 1 / 1;
92
+ justify-self: var(--push-content-items, flex-start);
93
+ max-inline-size: var(--push-content-max-inline-size);
94
+ width: 100%;
95
+ z-index: 1;
96
+
97
+ @media (--sm) {
98
+ text-align: var(--push-content-align);
99
+ }
100
+
101
+ > * {
102
+ margin-block: 0;
103
+ }
104
+
105
+ /* The button keeps its own width rather than stretching across the
106
+ box, and follows the text alignment, which applies from --sm up. */
107
+ > .btn {
108
+ align-self: start;
109
+
110
+ @media (--sm) {
111
+ align-self: var(--push-content-align, start);
112
+ }
113
+ }
114
+ }
115
+
116
+ .media {
117
+ height: 100%;
118
+ inset: 0;
119
+ position: absolute;
120
+ width: 100%;
121
+ }
122
+
123
+ .surtitle {
124
+ margin-block-end: calc(var(--push-content-gap) * -1);
125
+ }
126
+
127
+ .btn::after {
128
+ content: '';
129
+ inset: 0;
130
+ position: absolute;
131
+ }
132
+ }
133
+
134
+ /* Card: the text box takes the look of a .card. Each value reads an optional
135
+ --push-card-* first, unset by default, so push cards can be restyled
136
+ without touching the cards themselves. */
137
+ .push-card > .content {
138
+ background-color: var(--push-card-color-background, var(--card-color-background));
139
+ border-color: var(--push-card-color-border, var(--card-color-border));
140
+ border-radius: var(--push-card-border-radius, var(--card-border-radius));
141
+ border-style: var(--push-card-border-style, var(--card-border-style));
142
+ border-width: var(--push-card-border-width, var(--card-border-width));
143
+ box-shadow: var(--push-card-shadow, var(--card-shadow));
144
+ color: var(--push-card-color-text, var(--card-color-text));
145
+ padding-block: var(--push-card-padding-block, var(--card-padding-block));
146
+ padding-inline: var(--push-card-padding-inline, var(--card-padding-inline));
147
+
148
+ > .title {
149
+ color: var(--push-card-title-color, var(--card-title-color));
150
+ font-family: var(--push-card-title-font-family, var(--card-title-font-family));
151
+ font-size: var(--push-card-title-font-size, var(--card-title-font-size));
152
+ font-weight: var(--push-card-title-font-weight, var(--card-title-font-weight));
153
+ line-height: var(--push-card-title-line-height, var(--card-title-line-height));
154
+ }
155
+
156
+ @media (prefers-reduced-motion: no-preference) {
157
+ transition: var(--push-card-transition, var(--card-transition));
158
+ }
159
+ }
160
+
161
+ @media (hover: hover) and (pointer: fine) {
162
+ .push-card:hover > .content {
163
+ --push-card-shadow: var(--push-card-shadow-hover, var(--card-shadow-hover));
164
+ }
165
+ }
166
+
167
+ /* Darken */
168
+ .push-darken {
169
+ /* Logical gradient directions (flipped under RTL) */
170
+ --push-gradient-to-end: to right; /* toward inline-end */
171
+ --push-gradient-to-start: to left; /* toward inline-start */
172
+
173
+ /* The shadow comes from the edge the content sits against. Only this
174
+ variable changes from one variant to the next; the ::before below is
175
+ the single place that paints it. */
176
+ --push-darken: linear-gradient(var(--push-gradient-to-end), var(--push-gradient-color-from) 0%, var(--push-gradient-color-to) var(--push-gradient-stop));
177
+
178
+ &:dir(rtl) {
179
+ --push-gradient-to-end: to left;
180
+ --push-gradient-to-start: to right;
181
+ }
182
+
183
+ @media (--sm) {
184
+ &.push-end {
185
+ --push-darken: linear-gradient(var(--push-gradient-to-start), var(--push-gradient-color-from) 0%, var(--push-gradient-color-to) var(--push-gradient-stop));
186
+ }
187
+ }
188
+
189
+ /* A vertical offset tilts the gradient the same way, as in .hero.
190
+ Declared after the horizontal ones, so it wins when both are set. */
191
+ &.push-vertical-start {
192
+ --push-darken: linear-gradient(to bottom, var(--push-gradient-color-from) 0%, var(--push-gradient-color-to) var(--push-gradient-stop));
193
+ }
194
+ &.push-vertical-end {
195
+ --push-darken: linear-gradient(to top, var(--push-gradient-color-from) 0%, var(--push-gradient-color-to) var(--push-gradient-stop));
196
+ }
197
+
198
+ /* …except when the text carries its own background, or is centered: the
199
+ media is dimmed as a whole instead. */
200
+ &.push-card,
201
+ &.push-center {
202
+ --push-darken: transparent;
203
+
204
+ .media {
205
+ filter: brightness(var(--push-media-brightness));
206
+ }
207
+ }
208
+
209
+ /* Painted on the push itself, edge to edge: .content is only the text box.
210
+ Same layering as .hero: the overlay sits above the media, the content
211
+ above the overlay. */
212
+ &::before {
213
+ background: var(--push-darken);
214
+ content: '';
215
+ inset: 0;
216
+ pointer-events: none;
217
+ position: absolute;
218
+ z-index: 1;
219
+ }
220
+ }
221
+
222
+ /* Offset: horizontal position of the content */
223
+ .push-center {
224
+ @media (--sm) {
225
+ --push-content-items: center;
226
+ }
227
+ }
228
+
229
+ .push-end {
230
+ @media (--sm) {
231
+ --push-content-items: flex-end;
232
+ }
233
+ }
234
+
235
+ /* Text alignment */
236
+ .push-align-start {
237
+ --push-content-align: start;
238
+ }
239
+
240
+ .push-align-center {
241
+ --push-content-align: center;
242
+ }
243
+
244
+ .push-align-end {
245
+ --push-content-align: end;
246
+ }
247
+
248
+ /* Vertical position of the content (centered by default) */
249
+ .push-vertical-start {
250
+ --push-content-justify: flex-start;
251
+ }
252
+
253
+ .push-vertical-end {
254
+ --push-content-justify: flex-end;
255
+ }
256
+ }
package/css/index.css CHANGED
@@ -26,6 +26,7 @@
26
26
  @import 'components/nav-title.css';
27
27
  @import 'components/pagination.css';
28
28
  @import 'components/panel.css';
29
+ @import 'components/push.css';
29
30
  @import 'components/surtitle.css';
30
31
  @import 'components/video.css';
31
32
 
@@ -35,9 +35,9 @@ Each sets four properties together: background, border, their hover values, and
35
35
  ```css
36
36
  .btn-danger {
37
37
  --btn-color-background: var(--color-danger);
38
- --btn-color-background-hover: var(--color-danger-strong);
38
+ --btn-color-background-hover: var(--color-danger-hover);
39
39
  --btn-color-border: var(--color-danger);
40
- --btn-color-border-hover: var(--color-danger-strong);
40
+ --btn-color-border-hover: var(--color-danger-hover);
41
41
  --btn-color-text: var(--color-text-on-danger);
42
42
  }
43
43
  ```
@@ -10,7 +10,7 @@ This package writes to two layers.
10
10
 
11
11
  | Layer | Contents |
12
12
  | --- | --- |
13
- | `@layer components` | All 29 components |
13
+ | `@layer components` | All 30 components |
14
14
  | `@layer utilities` | `.scrollsnap`, the only utility |
15
15
 
16
16
  The full order, including layers owned by the sibling packages and by the project itself, is the **consuming project's** responsibility. Declare it once, at the very top of your entry stylesheet, before any `@import`:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  isIndex: false
3
3
  title: Content
4
- description: Alerts, badges, banners, cards, the items grid, labelled lists, media objects and surtitles.
4
+ description: Alerts, badges, banners, cards, the items grid, labelled lists, media objects, pushes and surtitles.
5
5
  weight: 5
6
6
  icon: card-text
7
7
  ---
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  isIndex: false
3
3
  title: Overview
4
- description: The eight content components, the three that are meant to nest, and the two colour patterns they follow.
4
+ description: The nine content components, the three that are meant to nest, and the two colour patterns they follow.
5
5
  weight: 1
6
6
  icon: book
7
7
  ---
8
8
 
9
- Eight components for laying out page content.
9
+ Nine components for laying out page content.
10
10
 
11
11
  | Page | Class | Purpose |
12
12
  | --- | --- | --- |
@@ -17,6 +17,7 @@ Eight components for laying out page content.
17
17
  | [Items](../items/) | `.items` | The responsive grid that holds cards |
18
18
  | [List](../list/) | `.list` | A compact labelled list |
19
19
  | [Media](../media/) | `.media` | The structural base for images and video |
20
+ | [Push](../push/) | `.push` | A video or image with text and a call to action laid over it |
20
21
  | [Surtitle](../surtitle/) | `.surtitle` | An eyebrow label above a heading |
21
22
 
22
23
  ## Three of them are meant to nest
@@ -0,0 +1,100 @@
1
+ ---
2
+ isIndex: false
3
+ title: Push
4
+ description: A video or image with text laid over it, how the push places its text box, the position and darken modifiers, and the card variant.
5
+ weight: 9
6
+ icon: image
7
+ ---
8
+
9
+ A video or an image with a surtitle, a title, some text and a call to action laid over it.
10
+
11
+ ```html
12
+ <div class="push push-darken">
13
+ <div class="content">
14
+ <p class="surtitle">Collection</p>
15
+ <h2 class="title">Push title</h2>
16
+ <div class="text"><p>Short text.</p></div>
17
+ <a class="btn" href="/collection">Discover</a>
18
+ </div>
19
+ <div class="media">
20
+ <picture>...</picture>
21
+ </div>
22
+ </div>
23
+ ```
24
+
25
+ | Child | Role |
26
+ | --- | --- |
27
+ | `.content` | The text box, placed inside the push |
28
+ | `.media` | The image or video, stretched behind the content, see [Media](../media/) |
29
+ | `.surtitle` | An optional eyebrow, pulled back by one gap, see [Surtitle](../surtitle/) |
30
+ | `.btn` | The call to action, whose `::after` makes the whole push clickable |
31
+
32
+ The markup is the same for every modifier: switching a push to a card, or moving its text, is a class change, never a change of structure.
33
+
34
+ ## The push carries the ratio
35
+
36
+ The push is a one-cell grid with the content padding. Two boxes share that cell: `.content`, placed by the position modifiers below, and an empty `::after` that carries the ratio, `--push-ratio-mobile` below `--sm` and `--push-ratio` from `--sm` up. Negative margins stretch that box over the padding, so the ratio is the push's own. `.media` is absolutely positioned and fills the push.
37
+
38
+ The push takes the taller of the two boxes: a text that needs more room than the ratio leaves grows the push instead of being cut, even when a layout gives the push a `min-height` to fill its row. The single track is fixed to `minmax(0, 1fr)`, so the ratio box never widens it. To change the shape of one push, override the two ratio tokens on it rather than setting a height.
39
+
40
+ `.push::after` is therefore taken; `::before` is the darken overlay.
41
+
42
+ `--push-ratio-double` is not read here. It is a second ratio for layouts that set two pushes side by side and want a taller shape than a full-width one.
43
+
44
+ ## What the push hands down
45
+
46
+ Unless it has `.push-card`, a push sets the text, heading, link and surtitle colours from `--push-color`, and turns `.btn` into a light button that reads on a dark media. `--push-surtitle-color` overrides the surtitle colour alone; it is unset by default, and the surtitle falls back to `--push-color` at `--opacity-muted`.
47
+
48
+ It also reassigns two other namespaces:
49
+
50
+ ```css
51
+ --media-color-background: var(--push-color-background);
52
+ --ratio-video: auto;
53
+ ```
54
+
55
+ The background colour shows while the media loads, or behind an image with transparent areas. `--ratio-video: auto` lets a `.video` inside the push fill it instead of imposing its own ratio.
56
+
57
+ ## Position and alignment
58
+
59
+ The content is centred vertically and sits at the start horizontally by default. Three groups of modifiers move it.
60
+
61
+ | Modifier | Effect |
62
+ | --- | --- |
63
+ | `.push-center`, `.push-end` | Horizontal position of the content, from `--sm` up |
64
+ | `.push-align-start`, `-center`, `-end` | Text alignment, from `--sm` up |
65
+ | `.push-vertical-start`, `.push-vertical-end` | Vertical position of the content |
66
+
67
+ Each one only sets a private variable (`--push-content-items`, `--push-content-align`, `--push-content-justify`), so they combine freely. The content is capped at `--push-content-max-inline-size`, which narrows at `--sm` and `--lg` so that a line of text never spans a full-width push. Those steps follow the viewport, not the push: a layout that sets pushes side by side should reset the token to `100%` on them.
68
+
69
+ The `.btn` keeps its own width rather than stretching across the content, and follows the text alignment from `--sm` up, like the rest of the text.
70
+
71
+ A push stretched taller than its ratio, by a grid row or a carousel track, keeps its content where the modifiers put it within the whole height.
72
+
73
+ ## Darken
74
+
75
+ `.push-darken` paints a gradient between the media and the content, from the edge the content sits against: the start edge by default, the end edge with `.push-end`, the top or bottom with a vertical modifier. Directions flip under `:dir(rtl)`.
76
+
77
+ The gradient is painted by a `::before` on the push itself, edge to edge, since `.content` is only the text box.
78
+
79
+ With `.push-center` or `.push-card` there is no edge to darken from, so the whole media is dimmed instead, with `filter: brightness(var(--push-media-brightness))`. That token references `--hero-media-brightness` by default, so a push and a hero dim their media alike.
80
+
81
+ ## Card
82
+
83
+ `.push-card` puts the text on a background of its own, for media too busy to read text over. Nothing changes in the markup: `.content` itself takes the look of a [card](../card/).
84
+
85
+ It reads the card tokens for its background, border, radius, shadow, text colour, padding, title typography and transition. Each one goes through an optional `--push-card-*` of the same name first, unset by default:
86
+
87
+ ```css
88
+ border-radius: var(--push-card-border-radius, var(--card-border-radius));
89
+ padding-block: var(--push-card-padding-block, var(--card-padding-block));
90
+ ```
91
+
92
+ Override `--push-card-*` to restyle every push card without touching the cards themselves, or `--card-*` to change both. Its shadow swaps to `--push-card-shadow-hover`, falling back to `--card-shadow-hover`, when the push is hovered. A push card does not inherit `--push-color`, so its text keeps the card colours.
93
+
94
+ ## The whole-push link
95
+
96
+ `.btn::after` lays an overlay across the push, so a click anywhere on it follows the call to action.
97
+
98
+ `.content` stacks above the media and the darken overlay through its `z-index` as a grid item, without being positioned, so it is no containing block: the overlay resolves against the push and covers it entirely. Anything that does make `.content` a containing block, such as a `transform` or `translate` from an entrance animation, narrows the clickable area to the text box.
99
+
100
+ As with the [whole-card link](../card/#the-whole-card-link), a push with more than one interactive element needs a different structure, since the overlay covers the others.
@@ -2,7 +2,7 @@
2
2
  isIndex: false
3
3
  title: Surtitle
4
4
  description: An eyebrow label above a heading, tightened automatically when it sits inside a card.
5
- weight: 9
5
+ weight: 10
6
6
  icon: type-h2
7
7
  ---
8
8
 
package/docs/overview.md CHANGED
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  isIndex: false
3
3
  title: Overview
4
- description: The 29 components and 1 utility, how to install them, the import order inside the package, and what it deliberately leaves out.
4
+ description: The 30 components and 1 utility, how to install them, the import order inside the package, and what it deliberately leaves out.
5
5
  weight: 1
6
6
  icon: book
7
7
  ---
8
8
 
9
- `@uncinq/css-components` is the top of the stack: 29 components and 1 utility, written as plain CSS, reading their values from [@uncinq/component-tokens](../../component-tokens/) and adding no values of their own.
9
+ `@uncinq/css-components` is the top of the stack: 30 components and 1 utility, written as plain CSS, reading their values from [@uncinq/component-tokens](../../component-tokens/) and adding no values of their own.
10
10
 
11
11
  ```
12
12
  @uncinq/design-tokens primitive + semantic values
@@ -25,7 +25,7 @@ One page per component, grouped into six sections.
25
25
  | --- | --- |
26
26
  | [Buttons](../buttons/) | [`.btn`](../buttons/btn/), [`.btn-close`](../buttons/btn-close/), [`.btn-menu`](../buttons/btn-menu/), [`.btn-search`](../buttons/btn-search/), [`.btn-filter`](../buttons/btn-filter/), [`.btn-share`](../buttons/btn-share/), [`.btn-toc`](../buttons/btn-toc/), [`.btn-toggle-video`](../buttons/btn-toggle-video/) |
27
27
  | [Overlays](../overlays/) | [panel](../overlays/panel/), [`.modal`](../overlays/modal/), [`.drawer`](../overlays/drawer/), [`.dropdown`](../overlays/dropdown/) |
28
- | [Content](../content/) | [`.alert`](../content/alert/), [`.badge`](../content/badge/), [`.banner`](../content/banner/), [`.card`](../content/card/), [`.items`](../content/items/), [`.list`](../content/list/), [`.media`](../content/media/), [`.surtitle`](../content/surtitle/) |
28
+ | [Content](../content/) | [`.alert`](../content/alert/), [`.badge`](../content/badge/), [`.banner`](../content/banner/), [`.card`](../content/card/), [`.items`](../content/items/), [`.list`](../content/list/), [`.media`](../content/media/), [`.push`](../content/push/), [`.surtitle`](../content/surtitle/) |
29
29
  | [Navigation](../navigation/) | [`.nav`](../navigation/nav/), [`.nav-title`](../navigation/nav-title/), [`.nav-accessibility`](../navigation/nav-accessibility/), [`.breadcrumb-wrapper`](../navigation/breadcrumb/), [`.pagination`](../navigation/pagination/) |
30
30
  | [Embeds](../embeds/) | [`.embed`](../embeds/embed/), [`.video`](../embeds/video/), [`.map`](../embeds/map/) |
31
31
  | [Forms](../forms/) | [`.form`](../forms/form/), [`.form-check`](../forms/form-check/) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uncinq/css-components",
3
- "version": "1.16.6",
3
+ "version": "1.18.0",
4
4
  "description": "Framework-agnostic CSS component implementations.",
5
5
  "license": "MIT",
6
6
  "repository": {