@rescui/card 0.14.0 → 0.14.1-RUI-293-Update-menu-component-7a032357e.20
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 +5 -1
- package/lib/card.d.ts +75 -75
- package/lib/card.p.module.css.js +26 -26
- package/lib/index.css +53 -53
- package/lib/index.d.ts +3 -3
- package/lib/use-card-cn.d.ts +2 -2
- package/llm/cards.md +3650 -0
- package/llm/components/card-cn.md +1397 -0
- package/llm/index.md +3 -0
- package/package.json +9 -8
package/llm/cards.md
ADDED
|
@@ -0,0 +1,3650 @@
|
|
|
1
|
+
# Cards
|
|
2
|
+
|
|
3
|
+
Cards Catalog contains a large variety of card snippets to show different ways of building any type of card from scratch
|
|
4
|
+
|
|
5
|
+
## Cards
|
|
6
|
+
|
|
7
|
+
### Main types
|
|
8
|
+
|
|
9
|
+
In most cases, using the [cardCn className helper](/components/card-cn/) will be sufficient
|
|
10
|
+
|
|
11
|
+
Refer to [this guide](/components/card-cn/) to learn more about differences between cardCn and Cards Catalog
|
|
12
|
+
|
|
13
|
+
If you need more control over the card, you can use the following snippets, as well as a [step-by-step guide](/cards/guide/), to create a custom card from scratch:
|
|
14
|
+
|
|
15
|
+
#### Clickable cards
|
|
16
|
+
|
|
17
|
+
Cards with hover, active and focus states
|
|
18
|
+
|
|
19
|
+
[Open page](/cards/clickable/)
|
|
20
|
+
|
|
21
|
+
#### Tiles
|
|
22
|
+
|
|
23
|
+
Non-clickable cards
|
|
24
|
+
|
|
25
|
+
[Open page](/cards/tiles/)
|
|
26
|
+
|
|
27
|
+
### Other resources
|
|
28
|
+
|
|
29
|
+
#### Controls
|
|
30
|
+
|
|
31
|
+
Stateful carousel buttons, slider controls, etc.
|
|
32
|
+
|
|
33
|
+
[Open page](/cards/controls/)
|
|
34
|
+
|
|
35
|
+
#### Step-by-step guide
|
|
36
|
+
|
|
37
|
+
A description of what you should pay attention to when creating your own cards
|
|
38
|
+
|
|
39
|
+
[Open page](/cards/guide/)
|
|
40
|
+
|
|
41
|
+
#### cardCn
|
|
42
|
+
|
|
43
|
+
A ClassName Helper for creating cards with simple styles and some customizations
|
|
44
|
+
|
|
45
|
+
[Open page](/components/card-cn/)
|
|
46
|
+
|
|
47
|
+
## Clickable
|
|
48
|
+
|
|
49
|
+
### Base example
|
|
50
|
+
|
|
51
|
+
##### Details:
|
|
52
|
+
|
|
53
|
+
- Base example
|
|
54
|
+
- Using css variables to control the border
|
|
55
|
+
- Set border on the pseudo-element to avoid scaling bug when changing `border-width`
|
|
56
|
+
|
|
57
|
+
- [cardCn implementation](/components/card-cn/#default-clickable-card)
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import cn from 'classnames';
|
|
61
|
+
import { textCn } from '@rescui/typography';
|
|
62
|
+
|
|
63
|
+
const Demo = () => {
|
|
64
|
+
return (
|
|
65
|
+
<button type="button" className="base-card" style={{ maxWidth: 400 }}>
|
|
66
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
67
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
68
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
69
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
70
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
71
|
+
repellat tempore unde veniam?
|
|
72
|
+
</p>
|
|
73
|
+
</button>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
render(<Demo />);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```postcss
|
|
81
|
+
.base-card {
|
|
82
|
+
--my-card-border-width: 1px;
|
|
83
|
+
--my-card-border-color: var(--rs-color-black-t20);
|
|
84
|
+
|
|
85
|
+
position: relative;
|
|
86
|
+
|
|
87
|
+
display: block;
|
|
88
|
+
|
|
89
|
+
overflow: hidden;
|
|
90
|
+
|
|
91
|
+
box-sizing: border-box;
|
|
92
|
+
min-height: 0;
|
|
93
|
+
|
|
94
|
+
border: none;
|
|
95
|
+
border-radius: 8px;
|
|
96
|
+
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
text-align: inherit;
|
|
99
|
+
|
|
100
|
+
background-color: var(--rs-color-white);
|
|
101
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
102
|
+
--my-card-border-color
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
transition: box-shadow 100ms;
|
|
106
|
+
appearance: none;
|
|
107
|
+
padding: 24px;
|
|
108
|
+
|
|
109
|
+
&,
|
|
110
|
+
&:hover {
|
|
111
|
+
text-decoration: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* reset user agent stylesheet at Chrome */
|
|
115
|
+
&:focus-visible {
|
|
116
|
+
outline: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@media (hover: hover) {
|
|
120
|
+
&:hover {
|
|
121
|
+
--my-card-border-color: var(--rs-color-black);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&:focus-visible,
|
|
126
|
+
&:active {
|
|
127
|
+
--my-card-border-color: var(--rs-color-black);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&:active {
|
|
131
|
+
--my-card-border-width: 2px;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Dark theme
|
|
137
|
+
|
|
138
|
+
##### Details:
|
|
139
|
+
|
|
140
|
+
- Dark theme
|
|
141
|
+
- Using css variables to control the border
|
|
142
|
+
- Set border on the pseudo-element to avoid scaling bug when changing `border-width`
|
|
143
|
+
|
|
144
|
+
- [cardCn implementation](/components/card-cn/#theme-dark)
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import cn from 'classnames';
|
|
148
|
+
import { textCn } from '@rescui/typography';
|
|
149
|
+
|
|
150
|
+
const Demo = () => {
|
|
151
|
+
return (
|
|
152
|
+
<div className="dark-wrapper">
|
|
153
|
+
<button type="button" className="dark-card" style={{ maxWidth: 400 }}>
|
|
154
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
155
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
156
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
157
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
158
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
159
|
+
repellat tempore unde veniam?
|
|
160
|
+
</p>
|
|
161
|
+
</button>
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
render(<Demo />);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```postcss
|
|
170
|
+
.dark-wrapper {
|
|
171
|
+
--rs-theme-dark: 1;
|
|
172
|
+
padding: 48px;
|
|
173
|
+
background: var(--rs-color-black);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.dark-card {
|
|
177
|
+
--my-card-border-width: 1px;
|
|
178
|
+
--my-card-border-color: var(--rs-color-white-t20);
|
|
179
|
+
|
|
180
|
+
position: relative;
|
|
181
|
+
|
|
182
|
+
display: block;
|
|
183
|
+
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
|
|
186
|
+
box-sizing: border-box;
|
|
187
|
+
min-height: 0;
|
|
188
|
+
|
|
189
|
+
border: none;
|
|
190
|
+
border-radius: 8px;
|
|
191
|
+
|
|
192
|
+
cursor: pointer;
|
|
193
|
+
text-align: inherit;
|
|
194
|
+
|
|
195
|
+
background-color: var(--rs-color-grey-90);
|
|
196
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
197
|
+
--my-card-border-color
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
transition: box-shadow 100ms;
|
|
201
|
+
appearance: none;
|
|
202
|
+
padding: 24px;
|
|
203
|
+
|
|
204
|
+
&,
|
|
205
|
+
&:hover {
|
|
206
|
+
text-decoration: none;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* reset user agent stylesheet at Chrome */
|
|
210
|
+
&:focus-visible {
|
|
211
|
+
outline: none;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@media (hover: hover) {
|
|
215
|
+
&:hover {
|
|
216
|
+
--my-card-border-color: var(--rs-color-white);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
&:focus-visible,
|
|
221
|
+
&:active {
|
|
222
|
+
--my-card-border-color: var(--rs-color-white);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
&:active {
|
|
226
|
+
--my-card-border-width: 2px;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Borderless example
|
|
232
|
+
|
|
233
|
+
##### Details:
|
|
234
|
+
|
|
235
|
+
- Borderless example
|
|
236
|
+
- States change background color
|
|
237
|
+
- [cardCn implementation](/components/card-cn/css/#borderless-card)
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import cn from 'classnames';
|
|
241
|
+
import { textCn } from '@rescui/typography';
|
|
242
|
+
|
|
243
|
+
const Demo = () => {
|
|
244
|
+
return (
|
|
245
|
+
<button type="button" className="borderless-card" style={{ maxWidth: 400 }}>
|
|
246
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
247
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
248
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
249
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
250
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
251
|
+
repellat tempore unde veniam?
|
|
252
|
+
</p>
|
|
253
|
+
</button>
|
|
254
|
+
);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
render(<Demo />);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```postcss
|
|
261
|
+
.borderless-card {
|
|
262
|
+
display: block;
|
|
263
|
+
|
|
264
|
+
overflow: hidden;
|
|
265
|
+
|
|
266
|
+
box-sizing: border-box;
|
|
267
|
+
min-height: 0;
|
|
268
|
+
|
|
269
|
+
border: none;
|
|
270
|
+
border-radius: 8px;
|
|
271
|
+
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
text-align: inherit;
|
|
274
|
+
|
|
275
|
+
background-color: var(--rs-color-grey-5);
|
|
276
|
+
|
|
277
|
+
transition: background-color 100ms;
|
|
278
|
+
appearance: none;
|
|
279
|
+
padding: 24px;
|
|
280
|
+
|
|
281
|
+
&,
|
|
282
|
+
&:hover {
|
|
283
|
+
text-decoration: none;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* reset user agent stylesheet at Chrome */
|
|
287
|
+
&:focus-visible {
|
|
288
|
+
outline: none;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
@media (hover: hover) {
|
|
292
|
+
&:hover {
|
|
293
|
+
background-color: var(--rs-color-grey-10);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
&:focus-visible {
|
|
298
|
+
background-color: var(--rs-color-grey-10);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
&:active {
|
|
302
|
+
background-color: var(--rs-color-grey-20);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### With glow hover
|
|
308
|
+
|
|
309
|
+
##### Details:
|
|
310
|
+
|
|
311
|
+
- Borderless example with `glow-hover` effect
|
|
312
|
+
- States don't change anything in css
|
|
313
|
+
- Hover and active states are covered by `glow-hover`
|
|
314
|
+
- [cardCn implementation](/components/card-cn/#with-glow-hover)
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import cn from 'classnames';
|
|
318
|
+
import { textCn } from '@rescui/typography';
|
|
319
|
+
import { useGlowHover } from '@rescui/use-glow-hover';
|
|
320
|
+
|
|
321
|
+
const Demo = () => {
|
|
322
|
+
const ref = useGlowHover({ preset: 'transparent-blue', enableBurst: true });
|
|
323
|
+
return (
|
|
324
|
+
<button
|
|
325
|
+
type="button"
|
|
326
|
+
ref={ref}
|
|
327
|
+
className="borderless-with-glow-hover-card"
|
|
328
|
+
style={{ maxWidth: 400 }}
|
|
329
|
+
>
|
|
330
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
331
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
332
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
333
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
334
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
335
|
+
repellat tempore unde veniam?
|
|
336
|
+
</p>
|
|
337
|
+
</button>
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
render(<Demo />);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
```postcss
|
|
345
|
+
.borderless-with-glow-hover-card {
|
|
346
|
+
display: block;
|
|
347
|
+
|
|
348
|
+
overflow: hidden;
|
|
349
|
+
|
|
350
|
+
box-sizing: border-box;
|
|
351
|
+
min-height: 0;
|
|
352
|
+
|
|
353
|
+
border: none;
|
|
354
|
+
border-radius: 8px;
|
|
355
|
+
|
|
356
|
+
cursor: pointer;
|
|
357
|
+
text-align: inherit;
|
|
358
|
+
|
|
359
|
+
background-color: var(--rs-color-grey-5);
|
|
360
|
+
|
|
361
|
+
transition: background-color 100ms;
|
|
362
|
+
appearance: none;
|
|
363
|
+
padding: 24px;
|
|
364
|
+
|
|
365
|
+
&,
|
|
366
|
+
&:hover {
|
|
367
|
+
text-decoration: none;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* reset user agent stylesheet at Chrome */
|
|
371
|
+
&:focus-visible {
|
|
372
|
+
outline: none;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
&:focus-visible {
|
|
376
|
+
background-color: var(--rs-color-grey-10);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### With action button inside
|
|
382
|
+
|
|
383
|
+
##### Details:
|
|
384
|
+
|
|
385
|
+
- With border via pseudo-element `::before`
|
|
386
|
+
|
|
387
|
+
- Forward states to the `Button` inside
|
|
388
|
+
|
|
389
|
+
- Card should have the `button` HTML tag
|
|
390
|
+
|
|
391
|
+
- Main action, like `onClick`, should be on the card element
|
|
392
|
+
|
|
393
|
+
- Use `notFocusable` property with `Button`
|
|
394
|
+
|
|
395
|
+
- Use only one action element inside
|
|
396
|
+
- [cardCn implementation](/components/card-cn/#with-action-button-inside)
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
import cn from 'classnames';
|
|
400
|
+
import { textCn } from '@rescui/typography';
|
|
401
|
+
import { Button } from '@rescui/button';
|
|
402
|
+
|
|
403
|
+
const Demo = () => {
|
|
404
|
+
return (
|
|
405
|
+
<button
|
|
406
|
+
type="button"
|
|
407
|
+
onClick={() => console.log('Card onClick called')}
|
|
408
|
+
className="action-button-inside-card"
|
|
409
|
+
style={{ maxWidth: 400 }}
|
|
410
|
+
>
|
|
411
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
412
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
413
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
414
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
415
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
416
|
+
repellat tempore unde veniam?
|
|
417
|
+
</p>
|
|
418
|
+
<div className="rs-docs-offset-top-24">
|
|
419
|
+
<Button notFocusable className="action-button-inside-card__button">
|
|
420
|
+
Click me
|
|
421
|
+
</Button>
|
|
422
|
+
</div>
|
|
423
|
+
</button>
|
|
424
|
+
);
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
render(<Demo />);
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
```postcss
|
|
431
|
+
@import '@rescui/button/lib/public-api.p.css';
|
|
432
|
+
|
|
433
|
+
.action-button-inside-card {
|
|
434
|
+
--my-card-border-width: 1px;
|
|
435
|
+
--my-card-border-color: var(--rs-color-black-t20);
|
|
436
|
+
|
|
437
|
+
position: relative;
|
|
438
|
+
|
|
439
|
+
display: block;
|
|
440
|
+
|
|
441
|
+
overflow: hidden;
|
|
442
|
+
|
|
443
|
+
box-sizing: border-box;
|
|
444
|
+
min-height: 0;
|
|
445
|
+
|
|
446
|
+
border: none;
|
|
447
|
+
border-radius: 8px;
|
|
448
|
+
|
|
449
|
+
cursor: pointer;
|
|
450
|
+
text-align: inherit;
|
|
451
|
+
|
|
452
|
+
background-color: var(--rs-color-white);
|
|
453
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
454
|
+
--my-card-border-color
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
transition: box-shadow 100ms;
|
|
458
|
+
appearance: none;
|
|
459
|
+
padding: 24px;
|
|
460
|
+
|
|
461
|
+
&,
|
|
462
|
+
&:hover {
|
|
463
|
+
text-decoration: none;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/* reset user agent stylesheet at Chrome */
|
|
467
|
+
&:focus-visible {
|
|
468
|
+
outline: none;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
@media (hover: hover) {
|
|
472
|
+
&:hover {
|
|
473
|
+
--my-card-border-color: var(--rs-color-black);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
&:focus-visible,
|
|
478
|
+
&:active {
|
|
479
|
+
--my-card-border-color: var(--rs-color-black);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
&:active {
|
|
483
|
+
--my-card-border-width: 2px;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
@media (hover: hover) {
|
|
487
|
+
&:hover &__button {
|
|
488
|
+
@mixin rs-button-state-hover;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
&:active &__button {
|
|
493
|
+
@mixin rs-button-state-active;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
&:focus-visible &__button {
|
|
497
|
+
@mixin rs-button-state-focus-visible;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
&:active:focus-visible:not(:hover) &__button {
|
|
501
|
+
@mixin rs-button-state-active-focus-visible-no-hover;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### With action link inside
|
|
507
|
+
|
|
508
|
+
##### Details:
|
|
509
|
+
|
|
510
|
+
- With border via pseudo-element `::before`
|
|
511
|
+
|
|
512
|
+
- Forward states to the `rs-link` inside
|
|
513
|
+
|
|
514
|
+
- Card should have the `a` HTML tag
|
|
515
|
+
|
|
516
|
+
- Main action, like `href`, should be on the card element
|
|
517
|
+
|
|
518
|
+
- `rs-link` should have `span` HTML tag
|
|
519
|
+
|
|
520
|
+
- Use only one action element inside
|
|
521
|
+
- [cardCn implementation](/components/card-cn/#with-action-link-inside)
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
import cn from 'classnames';
|
|
525
|
+
import { textCn } from '@rescui/typography';
|
|
526
|
+
|
|
527
|
+
const Demo = () => {
|
|
528
|
+
return (
|
|
529
|
+
<a
|
|
530
|
+
href="#with-action-link-inside"
|
|
531
|
+
className="action-link-inside-card"
|
|
532
|
+
style={{ maxWidth: 400 }}
|
|
533
|
+
>
|
|
534
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
535
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
536
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
537
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
538
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
539
|
+
repellat tempore unde veniam?
|
|
540
|
+
</p>
|
|
541
|
+
<div className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
542
|
+
<span
|
|
543
|
+
className={cn(
|
|
544
|
+
textCn('rs-link', { mode: 'standalone' }),
|
|
545
|
+
'action-link-inside-card__link'
|
|
546
|
+
)}
|
|
547
|
+
>
|
|
548
|
+
Click me
|
|
549
|
+
</span>
|
|
550
|
+
</div>
|
|
551
|
+
</a>
|
|
552
|
+
);
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
render(<Demo />);
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
```postcss
|
|
559
|
+
@import '@rescui/typography/lib/public-api.p.css';
|
|
560
|
+
|
|
561
|
+
.action-link-inside-card {
|
|
562
|
+
--my-card-border-width: 1px;
|
|
563
|
+
--my-card-border-color: var(--rs-color-black-t20);
|
|
564
|
+
|
|
565
|
+
position: relative;
|
|
566
|
+
|
|
567
|
+
display: block;
|
|
568
|
+
|
|
569
|
+
overflow: hidden;
|
|
570
|
+
|
|
571
|
+
box-sizing: border-box;
|
|
572
|
+
min-height: 0;
|
|
573
|
+
|
|
574
|
+
border: none;
|
|
575
|
+
border-radius: 8px;
|
|
576
|
+
|
|
577
|
+
cursor: pointer;
|
|
578
|
+
text-align: inherit;
|
|
579
|
+
|
|
580
|
+
background-color: var(--rs-color-white);
|
|
581
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
582
|
+
--my-card-border-color
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
transition: box-shadow 100ms;
|
|
586
|
+
appearance: none;
|
|
587
|
+
padding: 24px;
|
|
588
|
+
|
|
589
|
+
&,
|
|
590
|
+
&:hover {
|
|
591
|
+
text-decoration: none;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/* reset user agent stylesheet at Chrome */
|
|
595
|
+
&:focus-visible {
|
|
596
|
+
outline: none;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
@media (hover: hover) {
|
|
600
|
+
&:hover {
|
|
601
|
+
--my-card-border-color: var(--rs-color-black);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
&:focus-visible,
|
|
606
|
+
&:active {
|
|
607
|
+
--my-card-border-color: var(--rs-color-black);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
&:active {
|
|
611
|
+
--my-card-border-width: 2px;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
@media (hover: hover) {
|
|
615
|
+
&:hover &__link {
|
|
616
|
+
@mixin rs-typography-link-state-hover;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
&:focus-visible &__link {
|
|
621
|
+
@mixin rs-typography-link-state-focus-visible;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### Rock mode
|
|
627
|
+
|
|
628
|
+
##### Details:
|
|
629
|
+
|
|
630
|
+
- With static border on the main element (border width is constant)
|
|
631
|
+
|
|
632
|
+
- Decrease inner padding by border width value to compensate for the offset
|
|
633
|
+
- Change `border-color` to transparent on hover/active instead of changing its width
|
|
634
|
+
|
|
635
|
+
- Change background-color to contrast value on hover/active
|
|
636
|
+
- Use `--rs-theme-flip` css-variable to reverse theme for all RescUI components on hover/active
|
|
637
|
+
|
|
638
|
+
```tsx
|
|
639
|
+
import cn from 'classnames';
|
|
640
|
+
import { textCn } from '@rescui/typography';
|
|
641
|
+
|
|
642
|
+
const Demo = () => {
|
|
643
|
+
return (
|
|
644
|
+
<button type="button" className="rock-mode-card" style={{ maxWidth: 400 }}>
|
|
645
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
646
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
647
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
648
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
649
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
650
|
+
repellat tempore unde veniam?
|
|
651
|
+
</p>
|
|
652
|
+
</button>
|
|
653
|
+
);
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
render(<Demo />);
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
```postcss
|
|
660
|
+
.rock-mode-card {
|
|
661
|
+
--my-card-border-color: var(--rs-color-black-t20);
|
|
662
|
+
|
|
663
|
+
display: block;
|
|
664
|
+
|
|
665
|
+
overflow: hidden;
|
|
666
|
+
|
|
667
|
+
box-sizing: border-box;
|
|
668
|
+
min-height: 0;
|
|
669
|
+
|
|
670
|
+
border: none;
|
|
671
|
+
border-radius: 8px;
|
|
672
|
+
|
|
673
|
+
cursor: pointer;
|
|
674
|
+
text-align: inherit;
|
|
675
|
+
|
|
676
|
+
background-color: var(--rs-color-white);
|
|
677
|
+
box-shadow: inset 0 0 0 1px var(--my-card-border-color);
|
|
678
|
+
|
|
679
|
+
transition: color 100ms, background-color 100ms, box-shadow 100ms;
|
|
680
|
+
appearance: none;
|
|
681
|
+
padding: 24px;
|
|
682
|
+
|
|
683
|
+
&,
|
|
684
|
+
&:hover {
|
|
685
|
+
text-decoration: none;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* reset user agent stylesheet at Chrome */
|
|
689
|
+
&:focus-visible {
|
|
690
|
+
outline: none;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
@media (hover: hover) {
|
|
694
|
+
&:hover {
|
|
695
|
+
--rs-theme-flip: 1;
|
|
696
|
+
--my-card-border-color: transparent;
|
|
697
|
+
background-color: var(--rs-color-black);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
&:focus-visible,
|
|
702
|
+
&:active {
|
|
703
|
+
--rs-theme-flip: 1;
|
|
704
|
+
--my-card-border-color: transparent;
|
|
705
|
+
background-color: var(--rs-color-black);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
&:active {
|
|
709
|
+
--rs-theme-flip: 1;
|
|
710
|
+
--my-card-border-color: transparent;
|
|
711
|
+
background-color: var(--rs-color-grey-90);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
### Aligned grid with subgrid
|
|
717
|
+
|
|
718
|
+
##### Details:
|
|
719
|
+
|
|
720
|
+
- With border via pseudo-element `::before`
|
|
721
|
+
|
|
722
|
+
- Inside grid container
|
|
723
|
+
- Each card takes up two rows and applies subgrid to its children
|
|
724
|
+
- Cards text content lays on the second row, aligning with the content in the sibling cards
|
|
725
|
+
|
|
726
|
+
```tsx
|
|
727
|
+
import cn from 'classnames';
|
|
728
|
+
import { textCn } from '@rescui/typography';
|
|
729
|
+
|
|
730
|
+
const Demo = () => {
|
|
731
|
+
return (
|
|
732
|
+
<div className="aligned-grid">
|
|
733
|
+
<button type="button" className="aligned-grid__card">
|
|
734
|
+
<h3 className={textCn('rs-h3')}>
|
|
735
|
+
Some long card title which takes two lines
|
|
736
|
+
</h3>
|
|
737
|
+
<p className={cn(textCn('rs-text-2'), 'aligned-grid__content')}>
|
|
738
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
739
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
740
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
741
|
+
repellat tempore unde veniam?
|
|
742
|
+
</p>
|
|
743
|
+
</button>
|
|
744
|
+
<button type="button" className="aligned-grid__card">
|
|
745
|
+
<h3 className={textCn('rs-h3')}>Awesome card title</h3>
|
|
746
|
+
<p className={cn(textCn('rs-text-2'), 'aligned-grid__content')}>
|
|
747
|
+
The closest known nebula to Earth is called the Helix Nebula. It is
|
|
748
|
+
the remnant of a dying star — possibly one like the Sun. It is
|
|
749
|
+
approximately 700 light-years away from Earth.
|
|
750
|
+
</p>
|
|
751
|
+
</button>
|
|
752
|
+
<button type="button" className="aligned-grid__card">
|
|
753
|
+
<h3 className={textCn('rs-h3')}>Cool card title</h3>
|
|
754
|
+
<p className={cn(textCn('rs-text-2'), 'aligned-grid__content')}>
|
|
755
|
+
We help developers work faster by automating common, repetitive tasks
|
|
756
|
+
to enable them to stay focused on code design and the big picture. We
|
|
757
|
+
provide tools to explore and familiarize with code bases faster.
|
|
758
|
+
</p>
|
|
759
|
+
</button>
|
|
760
|
+
<button type="button" className="aligned-grid__card">
|
|
761
|
+
<h3 className={textCn('rs-h3')}>
|
|
762
|
+
Another long card title which takes two lines
|
|
763
|
+
</h3>
|
|
764
|
+
<p className={cn(textCn('rs-text-2'), 'aligned-grid__content')}>
|
|
765
|
+
Fëanor was a Ñoldorin elf and one of the Elven kindred that departed
|
|
766
|
+
from Valinor in the land of Aman, where they had lived with the Valar.
|
|
767
|
+
</p>
|
|
768
|
+
</button>
|
|
769
|
+
<button type="button" className="aligned-grid__card">
|
|
770
|
+
<h3 className={textCn('rs-h3')}>Neat card title</h3>
|
|
771
|
+
<p className={cn(textCn('rs-text-2'), 'aligned-grid__content')}>
|
|
772
|
+
It’s a dangerous business, Frodo, going out your door. You step onto
|
|
773
|
+
the road, and if you don’t keep your feet, there’s no knowing where
|
|
774
|
+
you might be swept off to.
|
|
775
|
+
</p>
|
|
776
|
+
</button>
|
|
777
|
+
</div>
|
|
778
|
+
);
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
render(<Demo />);
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
```postcss
|
|
785
|
+
.aligned-grid {
|
|
786
|
+
display: grid;
|
|
787
|
+
column-gap: 32px;
|
|
788
|
+
row-gap: 24px;
|
|
789
|
+
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
|
790
|
+
grid-template-rows: auto;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.aligned-grid__card {
|
|
794
|
+
--my-card-border-width: 1px;
|
|
795
|
+
--my-card-border-color: var(--rs-color-black-t20);
|
|
796
|
+
|
|
797
|
+
position: relative;
|
|
798
|
+
|
|
799
|
+
display: grid;
|
|
800
|
+
|
|
801
|
+
overflow: hidden;
|
|
802
|
+
|
|
803
|
+
box-sizing: border-box;
|
|
804
|
+
min-height: 0;
|
|
805
|
+
padding: 24px;
|
|
806
|
+
|
|
807
|
+
border: none;
|
|
808
|
+
border-radius: 8px;
|
|
809
|
+
|
|
810
|
+
cursor: pointer;
|
|
811
|
+
text-align: inherit;
|
|
812
|
+
|
|
813
|
+
background-color: var(--rs-color-white);
|
|
814
|
+
grid-template-columns: 1fr;
|
|
815
|
+
grid-template-rows: subgrid;
|
|
816
|
+
grid-row: auto / span 2;
|
|
817
|
+
row-gap: 24px;
|
|
818
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
819
|
+
--my-card-border-color
|
|
820
|
+
);
|
|
821
|
+
|
|
822
|
+
transition: box-shadow 100ms;
|
|
823
|
+
appearance: none;
|
|
824
|
+
|
|
825
|
+
&,
|
|
826
|
+
&:hover {
|
|
827
|
+
text-decoration: none;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/* reset user agent stylesheet at Chrome */
|
|
831
|
+
&:focus-visible {
|
|
832
|
+
outline: none;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
@media (hover: hover) {
|
|
836
|
+
&:hover {
|
|
837
|
+
--my-card-border-color: var(--rs-color-black);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
&:focus-visible,
|
|
842
|
+
&:active {
|
|
843
|
+
--my-card-border-color: var(--rs-color-black);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
&:active {
|
|
847
|
+
--my-card-border-width: 2px;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
.aligned-grid__content {
|
|
852
|
+
grid-row: 2 / span 1;
|
|
853
|
+
}
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
### Small cards
|
|
857
|
+
|
|
858
|
+
##### Details:
|
|
859
|
+
|
|
860
|
+
- With border via pseudo-element `::before`
|
|
861
|
+
|
|
862
|
+
- With colored bg and border
|
|
863
|
+
- `border-color` is constant, but the `background-color` changes on hover
|
|
864
|
+
- Text is center aligned
|
|
865
|
+
- Card's width is constant
|
|
866
|
+
|
|
867
|
+
```tsx
|
|
868
|
+
import cn from 'classnames';
|
|
869
|
+
import { textCn } from '@rescui/typography';
|
|
870
|
+
import apacheDerbyLogoSvg from '@/parts/cards/db-logos/apache-derby-logo.svg';
|
|
871
|
+
import azureLogoSvg from '@/parts/cards/db-logos/azure-logo.svg';
|
|
872
|
+
import h2DatabaseLogoSvg from '@/parts/cards/db-logos/h2-database-logo.svg';
|
|
873
|
+
import ibmDB2LogoSvg from '@/parts/cards/db-logos/ibm-db2-logo.svg';
|
|
874
|
+
import mongodbLogoSvg from '@/parts/cards/db-logos/mongodb-logo.svg';
|
|
875
|
+
import mysqlLogoSvg from '@/parts/cards/db-logos/mysql-logo.svg';
|
|
876
|
+
import redisLogoSvg from '@/parts/cards/db-logos/redis-logo.svg';
|
|
877
|
+
import sqliteLogoSvg from '@/parts/cards/db-logos/sqlite-logo.svg';
|
|
878
|
+
import sybaseLogoSvg from '@/parts/cards/db-logos/sybase-logo.svg';
|
|
879
|
+
import awsDynamodbLogo from '@/parts/cards/db-logos/aws-dynamodb-logo.svg';
|
|
880
|
+
|
|
881
|
+
const data = [
|
|
882
|
+
{ title: 'MySQL', logoSrc: mysqlLogoSvg.src },
|
|
883
|
+
{ title: 'MongoDB', logoSrc: mongodbLogoSvg.src },
|
|
884
|
+
{ title: 'Redis', logoSrc: redisLogoSvg.src },
|
|
885
|
+
{ title: 'Azure SQL', logoSrc: azureLogoSvg.src },
|
|
886
|
+
{ title: 'Amazon DynamoDB', logoSrc: awsDynamodbLogo.src },
|
|
887
|
+
{ title: 'SQLite', logoSrc: sqliteLogoSvg.src },
|
|
888
|
+
{ title: 'IBM Db2', logoSrc: ibmDB2LogoSvg.src },
|
|
889
|
+
{ title: 'H2', logoSrc: h2DatabaseLogoSvg.src },
|
|
890
|
+
{ title: 'Sybase ASE', logoSrc: sybaseLogoSvg.src },
|
|
891
|
+
{ title: 'Apache Derby', logoSrc: apacheDerbyLogoSvg.src }
|
|
892
|
+
];
|
|
893
|
+
|
|
894
|
+
const Demo = () => {
|
|
895
|
+
return (
|
|
896
|
+
<ul className={cn('small-cards', 'jb-offset-top-32')}>
|
|
897
|
+
{data.map(item => (
|
|
898
|
+
<li key={item.title}>
|
|
899
|
+
<a className={cn('small-cards__card')} href={'#small-cards'}>
|
|
900
|
+
<img
|
|
901
|
+
src={item.logoSrc}
|
|
902
|
+
alt="Database logo"
|
|
903
|
+
className="small-cards__card-logo"
|
|
904
|
+
/>
|
|
905
|
+
<h4 className={cn(textCn('rs-h5'), 'rs-docs-offset-top-12')}>
|
|
906
|
+
{item.title}
|
|
907
|
+
</h4>
|
|
908
|
+
</a>
|
|
909
|
+
</li>
|
|
910
|
+
))}
|
|
911
|
+
</ul>
|
|
912
|
+
);
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
render(<Demo />);
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
```postcss attached
|
|
919
|
+
.small-cards {
|
|
920
|
+
display: flex;
|
|
921
|
+
flex-wrap: wrap;
|
|
922
|
+
column-gap: 24px;
|
|
923
|
+
row-gap: 24px;
|
|
924
|
+
|
|
925
|
+
text-align: center;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.small-cards__card {
|
|
929
|
+
--my-card-border-width: 1px;
|
|
930
|
+
--my-card-border-color: var(--rs-color-primary-light-theme);
|
|
931
|
+
--my-card-background-color: var(--rs-color-white);
|
|
932
|
+
|
|
933
|
+
position: relative;
|
|
934
|
+
|
|
935
|
+
display: block;
|
|
936
|
+
|
|
937
|
+
overflow: hidden;
|
|
938
|
+
|
|
939
|
+
box-sizing: border-box;
|
|
940
|
+
width: 150px;
|
|
941
|
+
min-height: 0;
|
|
942
|
+
padding: 32px 8px;
|
|
943
|
+
|
|
944
|
+
border: none;
|
|
945
|
+
border-radius: 8px;
|
|
946
|
+
|
|
947
|
+
cursor: pointer;
|
|
948
|
+
text-align: inherit;
|
|
949
|
+
|
|
950
|
+
background-color: var(--my-card-background-color);
|
|
951
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
952
|
+
--my-card-border-color
|
|
953
|
+
);
|
|
954
|
+
|
|
955
|
+
transition: box-shadow 100ms;
|
|
956
|
+
appearance: none;
|
|
957
|
+
|
|
958
|
+
&,
|
|
959
|
+
&:hover {
|
|
960
|
+
text-decoration: none;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/* reset user agent stylesheet at Chrome */
|
|
964
|
+
&:focus-visible {
|
|
965
|
+
outline: none;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
@media (hover: hover) {
|
|
969
|
+
&:hover {
|
|
970
|
+
--my-card-background-color: var(--rs-color-primary-fog-light-theme);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
&:focus-visible,
|
|
975
|
+
&:active {
|
|
976
|
+
--my-card-background-color: var(--rs-color-primary-fog-light-theme);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
&:active {
|
|
980
|
+
--my-card-border-width: 2px;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.small-cards__card-logo {
|
|
985
|
+
height: 36px;
|
|
986
|
+
width: auto;
|
|
987
|
+
}
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
### Newsletter cards
|
|
991
|
+
|
|
992
|
+
##### Details:
|
|
993
|
+
|
|
994
|
+
- Has a large image
|
|
995
|
+
- Has an action button
|
|
996
|
+
- Border has a non-transparent color
|
|
997
|
+
- Padding controlled by css variables
|
|
998
|
+
- Action buttons are aligned with sibling tiles
|
|
999
|
+
- Use pseudo-element for border to show it above the content
|
|
1000
|
+
- [cardCn implementation](/components/card-cn/#with-image-via-img-tag)
|
|
1001
|
+
|
|
1002
|
+
```tsx
|
|
1003
|
+
import { textCn } from '@rescui/typography';
|
|
1004
|
+
import { Button } from '@rescui/button';
|
|
1005
|
+
import card1Image from '@/parts/cards/newsletter1.webp';
|
|
1006
|
+
import card2Image from '@/parts/cards/newsletter2.webp';
|
|
1007
|
+
|
|
1008
|
+
const NewsletterCard = ({ title, imageSrc }) => {
|
|
1009
|
+
return (
|
|
1010
|
+
<button type="button" className="newsletter-card">
|
|
1011
|
+
<div className="newsletter-card__header">
|
|
1012
|
+
<img className="newsletter-card__img" alt="keymaps" src={imageSrc} />
|
|
1013
|
+
<h3 className={textCn('rs-h3')}>{title}</h3>
|
|
1014
|
+
</div>
|
|
1015
|
+
<div className="newsletter-card__footer">
|
|
1016
|
+
<Button className="newsletter-card__button" notFocusable>
|
|
1017
|
+
Learn more
|
|
1018
|
+
</Button>
|
|
1019
|
+
</div>
|
|
1020
|
+
</button>
|
|
1021
|
+
);
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
const Demo = () => {
|
|
1025
|
+
return (
|
|
1026
|
+
<div className="newsletter-cards-container">
|
|
1027
|
+
<NewsletterCard
|
|
1028
|
+
title={'JetBrains Community Newsletter (Monthly) - join us now!'}
|
|
1029
|
+
imageSrc={card1Image.src}
|
|
1030
|
+
/>
|
|
1031
|
+
<NewsletterCard
|
|
1032
|
+
title={'Java Annotated Monthly'}
|
|
1033
|
+
imageSrc={card2Image.src}
|
|
1034
|
+
/>
|
|
1035
|
+
</div>
|
|
1036
|
+
);
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
render(<Demo />);
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
```postcss
|
|
1043
|
+
@import '@rescui/button/lib/public-api.p.css';
|
|
1044
|
+
|
|
1045
|
+
.newsletter-cards-container {
|
|
1046
|
+
display: grid;
|
|
1047
|
+
grid-template-rows: auto;
|
|
1048
|
+
grid-template-columns: repeat(2, 1fr);
|
|
1049
|
+
column-gap: 32px;
|
|
1050
|
+
row-gap: 24px;
|
|
1051
|
+
|
|
1052
|
+
@media (--sm) {
|
|
1053
|
+
grid-template-columns: repeat(1, 1fr);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
.newsletter-card {
|
|
1058
|
+
--my-card-border-width: 1px;
|
|
1059
|
+
--my-card-border-color: var(--rs-color-grey-20);
|
|
1060
|
+
--my-card-padding: 24px;
|
|
1061
|
+
|
|
1062
|
+
position: relative;
|
|
1063
|
+
|
|
1064
|
+
display: grid;
|
|
1065
|
+
overflow: hidden;
|
|
1066
|
+
|
|
1067
|
+
box-sizing: border-box;
|
|
1068
|
+
min-height: 0;
|
|
1069
|
+
padding: var(--my-card-padding);
|
|
1070
|
+
|
|
1071
|
+
border: none;
|
|
1072
|
+
border-radius: 8px;
|
|
1073
|
+
|
|
1074
|
+
cursor: pointer;
|
|
1075
|
+
text-align: inherit;
|
|
1076
|
+
|
|
1077
|
+
background-color: var(--rs-color-white);
|
|
1078
|
+
grid-template-columns: 1fr;
|
|
1079
|
+
grid-template-rows: subgrid;
|
|
1080
|
+
grid-row: auto / span 2;
|
|
1081
|
+
row-gap: 24px;
|
|
1082
|
+
|
|
1083
|
+
&,
|
|
1084
|
+
&:hover {
|
|
1085
|
+
text-decoration: none;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/* reset user agent stylesheet at Chrome */
|
|
1089
|
+
&:focus-visible {
|
|
1090
|
+
outline: none;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
@media (hover: hover) {
|
|
1094
|
+
&:hover {
|
|
1095
|
+
--my-card-border-color: var(--rs-color-black);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
&:focus-visible,
|
|
1100
|
+
&:active {
|
|
1101
|
+
--my-card-border-color: var(--rs-color-black);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
&:active {
|
|
1105
|
+
--my-card-border-width: 2px;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
&::after {
|
|
1109
|
+
position: absolute;
|
|
1110
|
+
top: 0;
|
|
1111
|
+
right: 0;
|
|
1112
|
+
bottom: 0;
|
|
1113
|
+
left: 0;
|
|
1114
|
+
|
|
1115
|
+
display: block;
|
|
1116
|
+
|
|
1117
|
+
border-radius: inherit;
|
|
1118
|
+
|
|
1119
|
+
content: '';
|
|
1120
|
+
pointer-events: none;
|
|
1121
|
+
|
|
1122
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
1123
|
+
--my-card-border-color
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
@media (hover: hover) {
|
|
1128
|
+
&:hover &__button {
|
|
1129
|
+
@mixin rs-button-state-hover;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
&:active &__button {
|
|
1134
|
+
@mixin rs-button-state-active;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
&:focus-visible &__button {
|
|
1138
|
+
@mixin rs-button-state-focus-visible;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
&:active:focus-visible:not(:hover) &__button {
|
|
1142
|
+
@mixin rs-button-state-active-focus-visible-no-hover;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.newsletter-card__img {
|
|
1147
|
+
position: relative;
|
|
1148
|
+
top: calc(-1 * var(--my-card-padding));
|
|
1149
|
+
left: calc(-1 * var(--my-card-padding));
|
|
1150
|
+
|
|
1151
|
+
display: flex;
|
|
1152
|
+
justify-content: center;
|
|
1153
|
+
align-items: center;
|
|
1154
|
+
|
|
1155
|
+
width: calc(100% + 2 * var(--my-card-padding));
|
|
1156
|
+
height: 212px;
|
|
1157
|
+
max-height: 212px;
|
|
1158
|
+
object-fit: cover;
|
|
1159
|
+
object-position: right;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
.newsletter-card__footer {
|
|
1163
|
+
grid-row: 2;
|
|
1164
|
+
display: flex;
|
|
1165
|
+
flex-wrap: wrap;
|
|
1166
|
+
justify-content: space-between;
|
|
1167
|
+
align-items: center;
|
|
1168
|
+
column-gap: 32px;
|
|
1169
|
+
row-gap: 24px;
|
|
1170
|
+
}
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
## Tiles
|
|
1174
|
+
|
|
1175
|
+
### Base example
|
|
1176
|
+
|
|
1177
|
+
##### Details:
|
|
1178
|
+
|
|
1179
|
+
- Base example
|
|
1180
|
+
- Has no dynamic states - its a static `div` element
|
|
1181
|
+
|
|
1182
|
+
- [cardCn implementation](/components/card-cn/#default-card)
|
|
1183
|
+
|
|
1184
|
+
```tsx
|
|
1185
|
+
import cn from 'classnames';
|
|
1186
|
+
import { textCn } from '@rescui/typography';
|
|
1187
|
+
|
|
1188
|
+
const Demo = () => {
|
|
1189
|
+
return (
|
|
1190
|
+
<div className="base-tile" style={{ maxWidth: 400 }}>
|
|
1191
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
1192
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
1193
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
1194
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
1195
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
1196
|
+
repellat tempore unde veniam?
|
|
1197
|
+
</p>
|
|
1198
|
+
</div>
|
|
1199
|
+
);
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
render(<Demo />);
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
```postcss
|
|
1206
|
+
.base-tile {
|
|
1207
|
+
position: relative;
|
|
1208
|
+
|
|
1209
|
+
display: block;
|
|
1210
|
+
|
|
1211
|
+
box-sizing: border-box;
|
|
1212
|
+
min-height: 0;
|
|
1213
|
+
|
|
1214
|
+
border: none;
|
|
1215
|
+
border-radius: 8px;
|
|
1216
|
+
|
|
1217
|
+
background-color: var(--rs-color-white);
|
|
1218
|
+
box-shadow: inset 0 0 0 1px var(--rs-color-black-t20);
|
|
1219
|
+
|
|
1220
|
+
padding: 24px;
|
|
1221
|
+
}
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
### Small tiles
|
|
1225
|
+
|
|
1226
|
+
##### Details:
|
|
1227
|
+
|
|
1228
|
+
- Without border
|
|
1229
|
+
- With colored background
|
|
1230
|
+
- Static element without states
|
|
1231
|
+
- Text is center aligned
|
|
1232
|
+
- Tiles width is constant
|
|
1233
|
+
|
|
1234
|
+
```tsx
|
|
1235
|
+
import cn from 'classnames';
|
|
1236
|
+
import { textCn } from '@rescui/typography';
|
|
1237
|
+
import apacheDerbyLogoSvg from '@/parts/cards/db-logos/apache-derby-logo.svg';
|
|
1238
|
+
import azureLogoSvg from '@/parts/cards/db-logos/azure-logo.svg';
|
|
1239
|
+
import h2DatabaseLogoSvg from '@/parts/cards/db-logos/h2-database-logo.svg';
|
|
1240
|
+
import ibmDB2LogoSvg from '@/parts/cards/db-logos/ibm-db2-logo.svg';
|
|
1241
|
+
import mongodbLogoSvg from '@/parts/cards/db-logos/mongodb-logo.svg';
|
|
1242
|
+
import mysqlLogoSvg from '@/parts/cards/db-logos/mysql-logo.svg';
|
|
1243
|
+
import redisLogoSvg from '@/parts/cards/db-logos/redis-logo.svg';
|
|
1244
|
+
import sqliteLogoSvg from '@/parts/cards/db-logos/sqlite-logo.svg';
|
|
1245
|
+
import sybaseLogoSvg from '@/parts/cards/db-logos/sybase-logo.svg';
|
|
1246
|
+
import awsDynamodbLogo from '@/parts/cards/db-logos/aws-dynamodb-logo.svg';
|
|
1247
|
+
|
|
1248
|
+
const data = [
|
|
1249
|
+
{ title: 'MySQL', logoSrc: mysqlLogoSvg.src },
|
|
1250
|
+
{ title: 'MongoDB', logoSrc: mongodbLogoSvg.src },
|
|
1251
|
+
{ title: 'Redis', logoSrc: redisLogoSvg.src },
|
|
1252
|
+
{ title: 'Azure SQL', logoSrc: azureLogoSvg.src },
|
|
1253
|
+
{ title: 'Amazon DynamoDB', logoSrc: awsDynamodbLogo.src },
|
|
1254
|
+
{ title: 'SQLite', logoSrc: sqliteLogoSvg.src },
|
|
1255
|
+
{ title: 'IBM Db2', logoSrc: ibmDB2LogoSvg.src },
|
|
1256
|
+
{ title: 'H2', logoSrc: h2DatabaseLogoSvg.src },
|
|
1257
|
+
{ title: 'Sybase ASE', logoSrc: sybaseLogoSvg.src },
|
|
1258
|
+
{ title: 'Apache Derby', logoSrc: apacheDerbyLogoSvg.src }
|
|
1259
|
+
];
|
|
1260
|
+
|
|
1261
|
+
const Demo = () => {
|
|
1262
|
+
return (
|
|
1263
|
+
<ul className={cn('small-tiles', 'jb-offset-top-32')}>
|
|
1264
|
+
{data.map(item => (
|
|
1265
|
+
<li key={item.title}>
|
|
1266
|
+
<span className={cn('small-tiles__tile')}>
|
|
1267
|
+
<img
|
|
1268
|
+
src={item.logoSrc}
|
|
1269
|
+
alt="Database logo"
|
|
1270
|
+
className="small-tiles__tile-logo"
|
|
1271
|
+
/>
|
|
1272
|
+
<h4 className={cn(textCn('rs-h5'), 'rs-docs-offset-top-12')}>
|
|
1273
|
+
{item.title}
|
|
1274
|
+
</h4>
|
|
1275
|
+
</span>
|
|
1276
|
+
</li>
|
|
1277
|
+
))}
|
|
1278
|
+
</ul>
|
|
1279
|
+
);
|
|
1280
|
+
};
|
|
1281
|
+
|
|
1282
|
+
render(<Demo />);
|
|
1283
|
+
```
|
|
1284
|
+
|
|
1285
|
+
```postcss
|
|
1286
|
+
.small-tiles {
|
|
1287
|
+
display: flex;
|
|
1288
|
+
flex-wrap: wrap;
|
|
1289
|
+
column-gap: 24px;
|
|
1290
|
+
row-gap: 24px;
|
|
1291
|
+
|
|
1292
|
+
text-align: center;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
.small-tiles__tile {
|
|
1296
|
+
position: relative;
|
|
1297
|
+
|
|
1298
|
+
display: block;
|
|
1299
|
+
|
|
1300
|
+
overflow: hidden;
|
|
1301
|
+
|
|
1302
|
+
box-sizing: border-box;
|
|
1303
|
+
width: 150px;
|
|
1304
|
+
min-height: 0;
|
|
1305
|
+
padding: 32px 8px;
|
|
1306
|
+
|
|
1307
|
+
border: none;
|
|
1308
|
+
border-radius: 8px;
|
|
1309
|
+
|
|
1310
|
+
background-color: var(--rs-color-primary-fog-light-theme);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
.small-tiles__tile-logo {
|
|
1314
|
+
height: 36px;
|
|
1315
|
+
width: auto;
|
|
1316
|
+
}
|
|
1317
|
+
```
|
|
1318
|
+
|
|
1319
|
+
### Borderless tile
|
|
1320
|
+
|
|
1321
|
+
##### Details:
|
|
1322
|
+
|
|
1323
|
+
- Without border
|
|
1324
|
+
- With background
|
|
1325
|
+
- Static element without states
|
|
1326
|
+
|
|
1327
|
+
```tsx
|
|
1328
|
+
import cn from 'classnames';
|
|
1329
|
+
import { textCn } from '@rescui/typography';
|
|
1330
|
+
|
|
1331
|
+
const Demo = () => {
|
|
1332
|
+
return (
|
|
1333
|
+
<div className="borderless-tile" style={{ maxWidth: 400 }}>
|
|
1334
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
1335
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
1336
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
1337
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
1338
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
1339
|
+
repellat tempore unde veniam?
|
|
1340
|
+
</p>
|
|
1341
|
+
</div>
|
|
1342
|
+
);
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
render(<Demo />);
|
|
1346
|
+
```
|
|
1347
|
+
|
|
1348
|
+
```postcss
|
|
1349
|
+
.borderless-tile {
|
|
1350
|
+
position: relative;
|
|
1351
|
+
|
|
1352
|
+
display: block;
|
|
1353
|
+
|
|
1354
|
+
box-sizing: border-box;
|
|
1355
|
+
min-height: 0;
|
|
1356
|
+
|
|
1357
|
+
border: none;
|
|
1358
|
+
border-radius: 8px;
|
|
1359
|
+
|
|
1360
|
+
background-color: var(--rs-color-black-t5);
|
|
1361
|
+
|
|
1362
|
+
padding: 24px;
|
|
1363
|
+
}
|
|
1364
|
+
```
|
|
1365
|
+
|
|
1366
|
+
### Large content
|
|
1367
|
+
|
|
1368
|
+
##### Details:
|
|
1369
|
+
|
|
1370
|
+
- Large header, large text
|
|
1371
|
+
- Content is center aligned
|
|
1372
|
+
- Large image below
|
|
1373
|
+
|
|
1374
|
+
```tsx
|
|
1375
|
+
import cn from 'classnames';
|
|
1376
|
+
import { textCn } from '@rescui/typography';
|
|
1377
|
+
import tileImage from '@/parts/cards/keymaps.webp';
|
|
1378
|
+
|
|
1379
|
+
const Demo = () => {
|
|
1380
|
+
return (
|
|
1381
|
+
<div className="large-content-tile" style={{ maxWidth: 600 }}>
|
|
1382
|
+
<div className="large-content-tile__content">
|
|
1383
|
+
<h2 className={textCn('rs-h2')}>Keymaps</h2>
|
|
1384
|
+
<p className={cn(textCn('rs-text-1'), 'rs-docs-offset-top-24')}>
|
|
1385
|
+
Choose a familiar keymap or set a shortcut for any action.
|
|
1386
|
+
</p>
|
|
1387
|
+
</div>
|
|
1388
|
+
<div className="large-content-tile__img">
|
|
1389
|
+
<img alt="keymaps" src={tileImage.src} />
|
|
1390
|
+
</div>
|
|
1391
|
+
</div>
|
|
1392
|
+
);
|
|
1393
|
+
};
|
|
1394
|
+
|
|
1395
|
+
render(<Demo />);
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
```postcss
|
|
1399
|
+
.large-content-tile {
|
|
1400
|
+
position: relative;
|
|
1401
|
+
|
|
1402
|
+
display: block;
|
|
1403
|
+
|
|
1404
|
+
box-sizing: border-box;
|
|
1405
|
+
min-height: 0;
|
|
1406
|
+
|
|
1407
|
+
padding: 24px;
|
|
1408
|
+
|
|
1409
|
+
border: none;
|
|
1410
|
+
border-radius: 8px;
|
|
1411
|
+
|
|
1412
|
+
text-align: center;
|
|
1413
|
+
|
|
1414
|
+
background-color: var(--rs-color-black-t5);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
.large-content-tile__content {
|
|
1418
|
+
width: 80%;
|
|
1419
|
+
margin: 0 auto;
|
|
1420
|
+
|
|
1421
|
+
@media (--sm) {
|
|
1422
|
+
width: 100%;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
.large-content-tile__img {
|
|
1427
|
+
display: flex;
|
|
1428
|
+
overflow: hidden;
|
|
1429
|
+
|
|
1430
|
+
margin: 32px 12px 12px;
|
|
1431
|
+
|
|
1432
|
+
border-radius: 8px;
|
|
1433
|
+
filter: drop-shadow(0 10px 35px rgba(0, 0, 0, 0.2));
|
|
1434
|
+
|
|
1435
|
+
&,
|
|
1436
|
+
img {
|
|
1437
|
+
max-width: 100%;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
### Newsletter tiles
|
|
1443
|
+
|
|
1444
|
+
##### Details:
|
|
1445
|
+
|
|
1446
|
+
- Has a large image
|
|
1447
|
+
- Has a subscribe form
|
|
1448
|
+
- Border has a non-transparent color
|
|
1449
|
+
- Padding and border are controlled by css variables
|
|
1450
|
+
- Subscribe forms are aligned with sibling tiles
|
|
1451
|
+
- Use pseudo-element for border to show it above the content
|
|
1452
|
+
|
|
1453
|
+
```tsx
|
|
1454
|
+
import cn from 'classnames';
|
|
1455
|
+
import { textCn } from '@rescui/typography';
|
|
1456
|
+
import { Input } from '@rescui/input';
|
|
1457
|
+
import { Button } from '@rescui/button';
|
|
1458
|
+
import tile1Image from '@/parts/cards/newsletter1.webp';
|
|
1459
|
+
import tile2Image from '@/parts/cards/newsletter2.webp';
|
|
1460
|
+
|
|
1461
|
+
const NewsletterTile = ({ title, imageSrc }) => {
|
|
1462
|
+
return (
|
|
1463
|
+
<div className="newsletter-tile">
|
|
1464
|
+
<div className="newsletter-tile__header">
|
|
1465
|
+
<img className="newsletter-tile__img" alt="keymaps" src={imageSrc} />
|
|
1466
|
+
<h3 className={textCn('rs-h3')}>{title}</h3>
|
|
1467
|
+
</div>
|
|
1468
|
+
<div className="newsletter-tile__form">
|
|
1469
|
+
<Input placeholder="Your Email" type="email" />
|
|
1470
|
+
<p className={cn(textCn('rs-text-3'), 'rs-docs-offset-top-16')}>
|
|
1471
|
+
By submitting this form, I acknowledge that JetBrains s.r.o. may
|
|
1472
|
+
process my personal data I provided above for purposes explained above
|
|
1473
|
+
and may engage third parties in such processing. More details about
|
|
1474
|
+
the processing are in JetBrains Privacy Notice.
|
|
1475
|
+
</p>
|
|
1476
|
+
<div className={cn('newsletter-tile__footer', 'rs-docs-offset-top-24')}>
|
|
1477
|
+
<Button>Subscribe</Button>
|
|
1478
|
+
<span className={textCn('rs-text-2')}>
|
|
1479
|
+
<a
|
|
1480
|
+
href="#newsletter-tiles"
|
|
1481
|
+
className={textCn('rs-link', { mode: 'standalone' })}
|
|
1482
|
+
>
|
|
1483
|
+
View sample newsletter
|
|
1484
|
+
</a>
|
|
1485
|
+
</span>
|
|
1486
|
+
</div>
|
|
1487
|
+
</div>
|
|
1488
|
+
</div>
|
|
1489
|
+
);
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
const Demo = () => {
|
|
1493
|
+
return (
|
|
1494
|
+
<div className="newsletter-tiles-container">
|
|
1495
|
+
<NewsletterTile
|
|
1496
|
+
title={'JetBrains Community Newsletter (Monthly)'}
|
|
1497
|
+
imageSrc={tile1Image.src}
|
|
1498
|
+
/>
|
|
1499
|
+
<NewsletterTile
|
|
1500
|
+
title={'Java Annotated Monthly'}
|
|
1501
|
+
imageSrc={tile2Image.src}
|
|
1502
|
+
/>
|
|
1503
|
+
</div>
|
|
1504
|
+
);
|
|
1505
|
+
};
|
|
1506
|
+
|
|
1507
|
+
render(<Demo />);
|
|
1508
|
+
```
|
|
1509
|
+
|
|
1510
|
+
```postcss
|
|
1511
|
+
.newsletter-tiles-container {
|
|
1512
|
+
display: grid;
|
|
1513
|
+
grid-template-rows: auto;
|
|
1514
|
+
grid-template-columns: repeat(2, 1fr);
|
|
1515
|
+
column-gap: 32px;
|
|
1516
|
+
row-gap: 24px;
|
|
1517
|
+
|
|
1518
|
+
@media (--sm) {
|
|
1519
|
+
grid-template-columns: repeat(1, 1fr);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
.newsletter-tile {
|
|
1524
|
+
--my-tile-padding: 24px;
|
|
1525
|
+
|
|
1526
|
+
position: relative;
|
|
1527
|
+
|
|
1528
|
+
display: grid;
|
|
1529
|
+
overflow: hidden;
|
|
1530
|
+
|
|
1531
|
+
box-sizing: border-box;
|
|
1532
|
+
min-height: 0;
|
|
1533
|
+
padding: var(--my-tile-padding);
|
|
1534
|
+
|
|
1535
|
+
border: none;
|
|
1536
|
+
border-radius: 8px;
|
|
1537
|
+
|
|
1538
|
+
background-color: var(--rs-color-white);
|
|
1539
|
+
grid-template-columns: 1fr;
|
|
1540
|
+
grid-template-rows: subgrid;
|
|
1541
|
+
grid-row: auto / span 2;
|
|
1542
|
+
row-gap: 16px;
|
|
1543
|
+
|
|
1544
|
+
&::after {
|
|
1545
|
+
position: absolute;
|
|
1546
|
+
top: 0;
|
|
1547
|
+
right: 0;
|
|
1548
|
+
bottom: 0;
|
|
1549
|
+
left: 0;
|
|
1550
|
+
|
|
1551
|
+
display: block;
|
|
1552
|
+
|
|
1553
|
+
border-radius: inherit;
|
|
1554
|
+
|
|
1555
|
+
content: '';
|
|
1556
|
+
pointer-events: none;
|
|
1557
|
+
|
|
1558
|
+
box-shadow: inset 0 0 0 1px var(--rs-color-grey-20);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
.newsletter-tile__form {
|
|
1563
|
+
grid-row: 2;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
.newsletter-tile__img {
|
|
1567
|
+
position: relative;
|
|
1568
|
+
top: calc(-1 * var(--my-tile-padding));
|
|
1569
|
+
left: calc(-1 * var(--my-tile-padding));
|
|
1570
|
+
|
|
1571
|
+
display: flex;
|
|
1572
|
+
justify-content: center;
|
|
1573
|
+
align-items: center;
|
|
1574
|
+
|
|
1575
|
+
width: calc(100% + 2 * var(--my-tile-padding));
|
|
1576
|
+
height: 212px;
|
|
1577
|
+
max-height: 212px;
|
|
1578
|
+
object-fit: cover;
|
|
1579
|
+
object-position: right;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
.newsletter-tile__footer {
|
|
1583
|
+
display: flex;
|
|
1584
|
+
flex-wrap: wrap;
|
|
1585
|
+
justify-content: space-between;
|
|
1586
|
+
align-items: center;
|
|
1587
|
+
column-gap: 32px;
|
|
1588
|
+
row-gap: 24px;
|
|
1589
|
+
}
|
|
1590
|
+
```
|
|
1591
|
+
|
|
1592
|
+
### Masonry grid
|
|
1593
|
+
|
|
1594
|
+
##### Details:
|
|
1595
|
+
|
|
1596
|
+
- Tiles are aligned using `grid-template-areas` CSS property
|
|
1597
|
+
|
|
1598
|
+
- Grid areas are forwarded to CSS using `--tile-id` CSS variable
|
|
1599
|
+
|
|
1600
|
+
- Flat tiles layout structure allows for easy adaptive changes compared to the two-column flex layout
|
|
1601
|
+
|
|
1602
|
+
```tsx
|
|
1603
|
+
import cn from 'classnames';
|
|
1604
|
+
import { textCn } from '@rescui/typography';
|
|
1605
|
+
|
|
1606
|
+
const MasonryTile = ({ name, position, children, tileId }) => {
|
|
1607
|
+
return (
|
|
1608
|
+
<div className="masonry-tile" style={{ '--tile-id': tileId }}>
|
|
1609
|
+
<h3 className={textCn('rs-h3')}>{name}</h3>
|
|
1610
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-8')}>
|
|
1611
|
+
{position}
|
|
1612
|
+
</p>
|
|
1613
|
+
<div
|
|
1614
|
+
className={cn(
|
|
1615
|
+
textCn('rs-text-2', { hardness: 'hard', paragraphOffsetAuto: true }),
|
|
1616
|
+
'rs-docs-offset-top-24'
|
|
1617
|
+
)}
|
|
1618
|
+
>
|
|
1619
|
+
{children}
|
|
1620
|
+
</div>
|
|
1621
|
+
</div>
|
|
1622
|
+
);
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1625
|
+
const Demo = () => {
|
|
1626
|
+
return (
|
|
1627
|
+
<div className="masonry-tiles-container">
|
|
1628
|
+
<MasonryTile
|
|
1629
|
+
tileId="stephan"
|
|
1630
|
+
name="Stephan Janssen"
|
|
1631
|
+
position="Founder at DevOxx, Co-founder Voxxed"
|
|
1632
|
+
>
|
|
1633
|
+
Fully integrating a chatbot into your favourite IDE significantly
|
|
1634
|
+
increases development productivity. No more toggling between platforms
|
|
1635
|
+
like ChatGPT – now you have an AI assistant embedded directly within
|
|
1636
|
+
your development environment. This allows for real-time code
|
|
1637
|
+
discussions, code reviews, and even intelligent name suggestions during
|
|
1638
|
+
refactoring tasks, among other features. AI Assistant takes software
|
|
1639
|
+
development to a whole new level, and it's clear that JetBrains has only
|
|
1640
|
+
scratched the surface of its potential. Exciting times to be a
|
|
1641
|
+
developer!
|
|
1642
|
+
</MasonryTile>
|
|
1643
|
+
<MasonryTile
|
|
1644
|
+
tileId="duncan"
|
|
1645
|
+
name="Duncan McGregor"
|
|
1646
|
+
position="Software Developer, Author of Java to Kotlin"
|
|
1647
|
+
>
|
|
1648
|
+
<p>
|
|
1649
|
+
It’s hard to know what effect AI will have on our jobs as professional
|
|
1650
|
+
developers, but right now JetBrains AI Assistant is making me more
|
|
1651
|
+
productive by removing drudgery. It’s better than I am at writing Bash
|
|
1652
|
+
to automate build and deployment tasks, and when Bash gets too
|
|
1653
|
+
fragile, it does a good job of converting scripts into Python. It is
|
|
1654
|
+
impressive when refactoring small routines too.
|
|
1655
|
+
</p>
|
|
1656
|
+
<p>
|
|
1657
|
+
Sometimes its results aren’t to my taste, but then we can have a chat
|
|
1658
|
+
about my preferred style and it will modify its suggestions
|
|
1659
|
+
accordingly. I also appreciate that the user interface makes it clear
|
|
1660
|
+
what information is being sent to the model, and that I have control
|
|
1661
|
+
over what is brought into my codebase.
|
|
1662
|
+
</p>
|
|
1663
|
+
</MasonryTile>
|
|
1664
|
+
<MasonryTile
|
|
1665
|
+
tileId="luca"
|
|
1666
|
+
name="Luca Nerlich"
|
|
1667
|
+
position="Tech Consultant, Nerlich / Puls GbR"
|
|
1668
|
+
>
|
|
1669
|
+
Absolute game-changer. Use it daily.
|
|
1670
|
+
</MasonryTile>
|
|
1671
|
+
<MasonryTile
|
|
1672
|
+
tileId="daniel"
|
|
1673
|
+
name="Daniel Lima"
|
|
1674
|
+
position="Senior Data Visualization Specialist"
|
|
1675
|
+
>
|
|
1676
|
+
This plugin is awesome, it is something that I did not know I needed,
|
|
1677
|
+
and now I can't live without it. Superb job, and the recommendations are
|
|
1678
|
+
better or fit better than other AI assistants.
|
|
1679
|
+
</MasonryTile>
|
|
1680
|
+
<MasonryTile
|
|
1681
|
+
tileId="clemens"
|
|
1682
|
+
name="Clemens Mattner"
|
|
1683
|
+
position="Software Engineer"
|
|
1684
|
+
>
|
|
1685
|
+
Really love it! This assistant helped me migrate some controls from
|
|
1686
|
+
Bootstrap to MudBlazor. Sometimes it writes my commit messages, if I'm
|
|
1687
|
+
too lazy. This assistant is a really good helper at work and makes life
|
|
1688
|
+
easier.
|
|
1689
|
+
</MasonryTile>
|
|
1690
|
+
</div>
|
|
1691
|
+
);
|
|
1692
|
+
};
|
|
1693
|
+
|
|
1694
|
+
render(<Demo />);
|
|
1695
|
+
```
|
|
1696
|
+
|
|
1697
|
+
```postcss
|
|
1698
|
+
.masonry-tiles-container {
|
|
1699
|
+
display: grid;
|
|
1700
|
+
grid-template-columns: repeat(2, 1fr);
|
|
1701
|
+
grid-template-rows: auto;
|
|
1702
|
+
grid-template-areas:
|
|
1703
|
+
'stephan luca'
|
|
1704
|
+
'stephan luca'
|
|
1705
|
+
'stephan daniel'
|
|
1706
|
+
'duncan daniel'
|
|
1707
|
+
'duncan clemens'
|
|
1708
|
+
'duncan clemens';
|
|
1709
|
+
column-gap: 32px;
|
|
1710
|
+
row-gap: 32px;
|
|
1711
|
+
|
|
1712
|
+
@media (--md) {
|
|
1713
|
+
grid-template-columns: 1fr;
|
|
1714
|
+
grid-template-rows: auto;
|
|
1715
|
+
grid-template-areas: none;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
.masonry-tile {
|
|
1720
|
+
--rs-theme-dark: 1;
|
|
1721
|
+
|
|
1722
|
+
position: relative;
|
|
1723
|
+
|
|
1724
|
+
display: block;
|
|
1725
|
+
grid-area: var(--tile-id);
|
|
1726
|
+
|
|
1727
|
+
box-sizing: border-box;
|
|
1728
|
+
min-height: 0;
|
|
1729
|
+
max-width: 600px;
|
|
1730
|
+
|
|
1731
|
+
padding: 24px;
|
|
1732
|
+
|
|
1733
|
+
border: none;
|
|
1734
|
+
border-radius: 8px;
|
|
1735
|
+
|
|
1736
|
+
background-color: var(--rs-color-primary-light-theme);
|
|
1737
|
+
|
|
1738
|
+
@media (--md) {
|
|
1739
|
+
grid-area: auto;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
```
|
|
1743
|
+
|
|
1744
|
+
### "Buy" tile
|
|
1745
|
+
|
|
1746
|
+
##### Details:
|
|
1747
|
+
|
|
1748
|
+
- Big tiles with a lot of content
|
|
1749
|
+
- One or more tiles may have `StickyTag`
|
|
1750
|
+
|
|
1751
|
+
- Content inside tiles is aligned with siblings
|
|
1752
|
+
- `checkbox` aligned using subgrid
|
|
1753
|
+
|
|
1754
|
+
- Actions in the footer are nailed to the tile bottom
|
|
1755
|
+
- Tiles may contain `Collapse` inside
|
|
1756
|
+
|
|
1757
|
+
```tsx
|
|
1758
|
+
import cn from 'classnames';
|
|
1759
|
+
import { textCn } from '@rescui/typography';
|
|
1760
|
+
import { StickyTag } from '@rescui/tag';
|
|
1761
|
+
import { Collapse } from '@rescui/collapse';
|
|
1762
|
+
import {
|
|
1763
|
+
AquaLogo,
|
|
1764
|
+
ClionLogo,
|
|
1765
|
+
CodeWithMeLogo,
|
|
1766
|
+
DatagripLogo,
|
|
1767
|
+
DataspellLogo,
|
|
1768
|
+
DotcoverLogo,
|
|
1769
|
+
DotmemoryLogo,
|
|
1770
|
+
DottraceLogo,
|
|
1771
|
+
GolandLogo,
|
|
1772
|
+
IntellijIdeaLogo,
|
|
1773
|
+
PhpstormLogo,
|
|
1774
|
+
PycharmLogo,
|
|
1775
|
+
ResharperLogo,
|
|
1776
|
+
ResharperCppLogo,
|
|
1777
|
+
RiderLogo,
|
|
1778
|
+
RubymineLogo,
|
|
1779
|
+
RustroverLogo,
|
|
1780
|
+
WebstormLogo
|
|
1781
|
+
} from '@jetbrains/logos/react';
|
|
1782
|
+
import { Checkbox } from '@rescui/checkbox';
|
|
1783
|
+
import { Button } from '@rescui/button';
|
|
1784
|
+
|
|
1785
|
+
const products = [
|
|
1786
|
+
{ title: 'Aqua', logo: <AquaLogo /> },
|
|
1787
|
+
{ title: 'CLion', logo: <ClionLogo /> },
|
|
1788
|
+
{ title: 'Code With Me', logo: <CodeWithMeLogo /> },
|
|
1789
|
+
{ title: 'DataGrip', logo: <DatagripLogo /> },
|
|
1790
|
+
{ title: 'DataSpell', logo: <DataspellLogo /> },
|
|
1791
|
+
{ title: 'dotCover', logo: <DotcoverLogo /> },
|
|
1792
|
+
{ title: 'dotMemory', logo: <DotmemoryLogo /> },
|
|
1793
|
+
{ title: 'dotTrace', logo: <DottraceLogo /> },
|
|
1794
|
+
{ title: 'GoLand', logo: <GolandLogo /> },
|
|
1795
|
+
{ title: 'IntelliJ IDEA', logo: <IntellijIdeaLogo /> },
|
|
1796
|
+
{ title: 'PhpStorm', logo: <PhpstormLogo /> },
|
|
1797
|
+
{ title: 'PyCharm', logo: <PycharmLogo /> },
|
|
1798
|
+
{ title: 'ReSharper', logo: <ResharperLogo /> },
|
|
1799
|
+
{ title: 'ReSharper C++', logo: <ResharperCppLogo /> },
|
|
1800
|
+
{ title: 'Rider', logo: <RiderLogo /> },
|
|
1801
|
+
{ title: 'RubyMine', logo: <RubymineLogo /> },
|
|
1802
|
+
{ title: 'RustRover', logo: <RustroverLogo /> },
|
|
1803
|
+
{ title: 'WebStorm', logo: <WebstormLogo /> }
|
|
1804
|
+
];
|
|
1805
|
+
|
|
1806
|
+
const FirstTile = () => {
|
|
1807
|
+
return (
|
|
1808
|
+
<div className="buy-tile">
|
|
1809
|
+
<div className="buy-tile__first-half">
|
|
1810
|
+
<h3 className={textCn('rs-h3')}>CLion</h3>
|
|
1811
|
+
<p
|
|
1812
|
+
className={cn(
|
|
1813
|
+
textCn('rs-text-2', { hardness: 'hard' }),
|
|
1814
|
+
'rs-docs-offset-top-16'
|
|
1815
|
+
)}
|
|
1816
|
+
>
|
|
1817
|
+
A smart cross-platform IDE for C and C++
|
|
1818
|
+
</p>
|
|
1819
|
+
<div
|
|
1820
|
+
className={cn('buy-tile-content__products', 'rs-docs-offset-top-8')}
|
|
1821
|
+
>
|
|
1822
|
+
<div
|
|
1823
|
+
className={cn(
|
|
1824
|
+
'buy-tile-content__products-item',
|
|
1825
|
+
textCn('rs-text-2')
|
|
1826
|
+
)}
|
|
1827
|
+
>
|
|
1828
|
+
<span className="buy-tile-content__products-logo">
|
|
1829
|
+
<ClionLogo />
|
|
1830
|
+
</span>
|
|
1831
|
+
<span>CLion</span>
|
|
1832
|
+
</div>
|
|
1833
|
+
</div>
|
|
1834
|
+
</div>
|
|
1835
|
+
<div className={'buy-tile__second-half'}>
|
|
1836
|
+
<Checkbox>
|
|
1837
|
+
Supercharge with{' '}
|
|
1838
|
+
<a href="#buy-tile" className={textCn('rs-link')}>
|
|
1839
|
+
JetBrains AI Pro
|
|
1840
|
+
</a>
|
|
1841
|
+
<p className={textCn('rs-text-2', { hardness: 'pale' })}>£158.00 (incl. VAT £189.60)</p>
|
|
1842
|
+
</Checkbox>
|
|
1843
|
+
<div className="rs-docs-offset-top-24">
|
|
1844
|
+
<div
|
|
1845
|
+
className={cn(
|
|
1846
|
+
'buy-tile-content__price-row',
|
|
1847
|
+
'buy-tile-content__price-row_baseline'
|
|
1848
|
+
)}
|
|
1849
|
+
>
|
|
1850
|
+
<div className={textCn('rs-text-3')}>
|
|
1851
|
+
<span className="buy-tile-content__nowrap">per user, </span>
|
|
1852
|
+
<span className="buy-tile-content__nowrap">first year</span>
|
|
1853
|
+
</div>
|
|
1854
|
+
<div
|
|
1855
|
+
className={cn(
|
|
1856
|
+
'buy-tile-content__nowrap',
|
|
1857
|
+
textCn('rs-subtitle-2')
|
|
1858
|
+
)}
|
|
1859
|
+
>
|
|
1860
|
+
£185<sup className="buy-tile-content__price-sup">.00</sup>
|
|
1861
|
+
</div>
|
|
1862
|
+
</div>
|
|
1863
|
+
<p
|
|
1864
|
+
className={cn('buy-tile-content__text-right', textCn('rs-text-3'))}
|
|
1865
|
+
>
|
|
1866
|
+
incl. VAT £222.80
|
|
1867
|
+
</p>
|
|
1868
|
+
<div
|
|
1869
|
+
className={cn(
|
|
1870
|
+
'buy-tile-content__price-row',
|
|
1871
|
+
'rs-docs-offset-top-24'
|
|
1872
|
+
)}
|
|
1873
|
+
>
|
|
1874
|
+
<div
|
|
1875
|
+
className={cn('buy-tile-content__nowrap', textCn('rs-text-3'))}
|
|
1876
|
+
>
|
|
1877
|
+
second year
|
|
1878
|
+
</div>
|
|
1879
|
+
<div className="buy-tile-content__text-right">
|
|
1880
|
+
<p className={textCn('rs-text-3', { hardness: 'hard' })}>
|
|
1881
|
+
£148.00
|
|
1882
|
+
</p>
|
|
1883
|
+
<p className={textCn('rs-text-3')}>incl. VAT £177.60</p>
|
|
1884
|
+
</div>
|
|
1885
|
+
</div>
|
|
1886
|
+
<div
|
|
1887
|
+
className={cn(
|
|
1888
|
+
'buy-tile-content__price-row',
|
|
1889
|
+
'rs-docs-offset-top-24'
|
|
1890
|
+
)}
|
|
1891
|
+
>
|
|
1892
|
+
<div
|
|
1893
|
+
className={cn('buy-tile-content__nowrap', textCn('rs-text-3'))}
|
|
1894
|
+
>
|
|
1895
|
+
third year onwards
|
|
1896
|
+
</div>
|
|
1897
|
+
<div className="buy-tile-content__text-right">
|
|
1898
|
+
<p className={textCn('rs-text-3', { hardness: 'hard' })}>
|
|
1899
|
+
£111.00
|
|
1900
|
+
</p>
|
|
1901
|
+
<p className={textCn('rs-text-3')}>incl. VAT £133.20</p>
|
|
1902
|
+
</div>
|
|
1903
|
+
</div>
|
|
1904
|
+
</div>
|
|
1905
|
+
<div
|
|
1906
|
+
className={cn(
|
|
1907
|
+
'buy-tile-content__footer-wrapper',
|
|
1908
|
+
'rs-docs-offset-top-48'
|
|
1909
|
+
)}
|
|
1910
|
+
>
|
|
1911
|
+
<div className={'buy-tile-content__footer'}>
|
|
1912
|
+
<Button href="#buy-tile">Buy</Button>
|
|
1913
|
+
<div className={cn(textCn('rs-text-2'), 'buy-tile-content__links')}>
|
|
1914
|
+
<a
|
|
1915
|
+
href="#buy-tile"
|
|
1916
|
+
className={textCn('rs-link', { mode: 'standalone' })}
|
|
1917
|
+
>
|
|
1918
|
+
Get quote
|
|
1919
|
+
</a>
|
|
1920
|
+
</div>
|
|
1921
|
+
</div>
|
|
1922
|
+
</div>
|
|
1923
|
+
</div>
|
|
1924
|
+
</div>
|
|
1925
|
+
);
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
const SecondTile = () => {
|
|
1929
|
+
return (
|
|
1930
|
+
<div className="buy-tile">
|
|
1931
|
+
<div className="buy-tile__first-half">
|
|
1932
|
+
<h3 className={textCn('rs-h3')}>All Products Pack</h3>
|
|
1933
|
+
<p
|
|
1934
|
+
className={cn(
|
|
1935
|
+
textCn('rs-text-2', { hardness: 'hard' }),
|
|
1936
|
+
'rs-docs-offset-top-16'
|
|
1937
|
+
)}
|
|
1938
|
+
>
|
|
1939
|
+
Get 12 IDEs, 3 extensions, 2 profilers, and a collaborative
|
|
1940
|
+
development service – all in one subscription.
|
|
1941
|
+
</p>
|
|
1942
|
+
<Collapse
|
|
1943
|
+
icon="arrow"
|
|
1944
|
+
title="Includes 18 tools"
|
|
1945
|
+
disableLeftPadding
|
|
1946
|
+
mode="clear"
|
|
1947
|
+
className="rs-docs-offset-top-12"
|
|
1948
|
+
>
|
|
1949
|
+
<div className="buy-tile-content__products">
|
|
1950
|
+
{products.map(({ title, logo }) => (
|
|
1951
|
+
<div
|
|
1952
|
+
key={title}
|
|
1953
|
+
className={cn(
|
|
1954
|
+
'buy-tile-content__products-item',
|
|
1955
|
+
textCn('rs-text-3')
|
|
1956
|
+
)}
|
|
1957
|
+
>
|
|
1958
|
+
<span className="buy-tile-content__products-logo">{logo}</span>
|
|
1959
|
+
<span>{title}</span>
|
|
1960
|
+
</div>
|
|
1961
|
+
))}
|
|
1962
|
+
</div>
|
|
1963
|
+
</Collapse>
|
|
1964
|
+
</div>
|
|
1965
|
+
<div className={'buy-tile__second-half'}>
|
|
1966
|
+
<Checkbox>
|
|
1967
|
+
Supercharge with{' '}
|
|
1968
|
+
<a href="#buy-tile" className={textCn('rs-link')}>
|
|
1969
|
+
JetBrains AI Pro
|
|
1970
|
+
</a>
|
|
1971
|
+
<p className={textCn('rs-text-2', { hardness: 'pale' })}>£158.00 (incl. VAT £189.60)</p>
|
|
1972
|
+
</Checkbox>
|
|
1973
|
+
<div className="rs-docs-offset-top-24">
|
|
1974
|
+
<div
|
|
1975
|
+
className={cn(
|
|
1976
|
+
'buy-tile-content__price-row',
|
|
1977
|
+
'buy-tile-content__price-row_baseline'
|
|
1978
|
+
)}
|
|
1979
|
+
>
|
|
1980
|
+
<div className={textCn('rs-text-3')}>
|
|
1981
|
+
<span className="buy-tile-content__nowrap">per user, </span>
|
|
1982
|
+
<span className="buy-tile-content__nowrap">first year</span>
|
|
1983
|
+
</div>
|
|
1984
|
+
<div
|
|
1985
|
+
className={cn(
|
|
1986
|
+
'buy-tile-content__nowrap',
|
|
1987
|
+
textCn('rs-subtitle-2')
|
|
1988
|
+
)}
|
|
1989
|
+
>
|
|
1990
|
+
£639<sup className="buy-tile-content__price-sup">.00</sup>
|
|
1991
|
+
</div>
|
|
1992
|
+
</div>
|
|
1993
|
+
<p
|
|
1994
|
+
className={cn('buy-tile-content__text-right', textCn('rs-text-3'))}
|
|
1995
|
+
>
|
|
1996
|
+
incl. VAT £766.80
|
|
1997
|
+
</p>
|
|
1998
|
+
<div
|
|
1999
|
+
className={cn(
|
|
2000
|
+
'buy-tile-content__price-row',
|
|
2001
|
+
'rs-docs-offset-top-24'
|
|
2002
|
+
)}
|
|
2003
|
+
>
|
|
2004
|
+
<div
|
|
2005
|
+
className={cn('buy-tile-content__nowrap', textCn('rs-text-3'))}
|
|
2006
|
+
>
|
|
2007
|
+
second year
|
|
2008
|
+
</div>
|
|
2009
|
+
<div className="buy-tile-content__text-right">
|
|
2010
|
+
<p className={textCn('rs-text-3', { hardness: 'hard' })}>
|
|
2011
|
+
£511.00
|
|
2012
|
+
</p>
|
|
2013
|
+
<p className={textCn('rs-text-3')}>incl. VAT £613.20</p>
|
|
2014
|
+
</div>
|
|
2015
|
+
</div>
|
|
2016
|
+
<div
|
|
2017
|
+
className={cn(
|
|
2018
|
+
'buy-tile-content__price-row',
|
|
2019
|
+
'rs-docs-offset-top-24'
|
|
2020
|
+
)}
|
|
2021
|
+
>
|
|
2022
|
+
<div
|
|
2023
|
+
className={cn('buy-tile-content__nowrap', textCn('rs-text-3'))}
|
|
2024
|
+
>
|
|
2025
|
+
third year onwards
|
|
2026
|
+
</div>
|
|
2027
|
+
<div className="buy-tile-content__text-right">
|
|
2028
|
+
<p className={textCn('rs-text-3', { hardness: 'hard' })}>
|
|
2029
|
+
£383.00
|
|
2030
|
+
</p>
|
|
2031
|
+
<p className={textCn('rs-text-3')}>incl. VAT £459.60</p>
|
|
2032
|
+
</div>
|
|
2033
|
+
</div>
|
|
2034
|
+
</div>
|
|
2035
|
+
<div
|
|
2036
|
+
className={cn(
|
|
2037
|
+
'buy-tile-content__footer-wrapper',
|
|
2038
|
+
'rs-docs-offset-top-48'
|
|
2039
|
+
)}
|
|
2040
|
+
>
|
|
2041
|
+
<div className={'buy-tile-content__footer'}>
|
|
2042
|
+
<Button href="#buy-tile">Buy</Button>
|
|
2043
|
+
<div className={cn(textCn('rs-text-2'), 'buy-tile-content__links')}>
|
|
2044
|
+
<a
|
|
2045
|
+
href="#buy-tile"
|
|
2046
|
+
className={textCn('rs-link', { mode: 'standalone' })}
|
|
2047
|
+
>
|
|
2048
|
+
Get quote
|
|
2049
|
+
</a>
|
|
2050
|
+
<a
|
|
2051
|
+
href="#buy-tile"
|
|
2052
|
+
className={textCn('rs-link', { mode: 'standalone' })}
|
|
2053
|
+
>
|
|
2054
|
+
Learn more
|
|
2055
|
+
</a>
|
|
2056
|
+
</div>
|
|
2057
|
+
</div>
|
|
2058
|
+
</div>
|
|
2059
|
+
</div>
|
|
2060
|
+
</div>
|
|
2061
|
+
);
|
|
2062
|
+
};
|
|
2063
|
+
|
|
2064
|
+
const Demo = () => {
|
|
2065
|
+
return (
|
|
2066
|
+
<div className={cn('buy-tiles-container', 'rs-docs-offset-top-16')}>
|
|
2067
|
+
<FirstTile />
|
|
2068
|
+
<StickyTag
|
|
2069
|
+
content={'Best offer'}
|
|
2070
|
+
color="white"
|
|
2071
|
+
backgroundColor="var(--rs-color-danger)"
|
|
2072
|
+
wrapperClassName="buy-tiles-container__tile-wrapper"
|
|
2073
|
+
>
|
|
2074
|
+
<SecondTile />
|
|
2075
|
+
</StickyTag>
|
|
2076
|
+
</div>
|
|
2077
|
+
);
|
|
2078
|
+
};
|
|
2079
|
+
|
|
2080
|
+
render(<Demo />);
|
|
2081
|
+
```
|
|
2082
|
+
|
|
2083
|
+
```postcss attached
|
|
2084
|
+
/* container styles */
|
|
2085
|
+
|
|
2086
|
+
.buy-tiles-container {
|
|
2087
|
+
display: grid;
|
|
2088
|
+
column-gap: 32px;
|
|
2089
|
+
row-gap: 32px;
|
|
2090
|
+
grid-template-columns: 5fr 5fr 2fr;
|
|
2091
|
+
grid-template-rows: auto;
|
|
2092
|
+
|
|
2093
|
+
@media (--md) {
|
|
2094
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
@media (--sm) {
|
|
2098
|
+
grid-template-columns: repeat(1, 1fr);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
.buy-tiles-container__tile-wrapper {
|
|
2103
|
+
display: grid;
|
|
2104
|
+
grid-template-rows: subgrid;
|
|
2105
|
+
grid-row: auto / span 2;
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
/* tile styles */
|
|
2109
|
+
|
|
2110
|
+
.buy-tile {
|
|
2111
|
+
position: relative;
|
|
2112
|
+
|
|
2113
|
+
display: grid;
|
|
2114
|
+
|
|
2115
|
+
box-sizing: border-box;
|
|
2116
|
+
min-height: 0;
|
|
2117
|
+
|
|
2118
|
+
padding: 24px;
|
|
2119
|
+
|
|
2120
|
+
border: none;
|
|
2121
|
+
border-radius: 8px;
|
|
2122
|
+
|
|
2123
|
+
background-color: var(--rs-color-white);
|
|
2124
|
+
grid-template-columns: 1fr;
|
|
2125
|
+
grid-template-rows: subgrid;
|
|
2126
|
+
grid-row: auto / span 2;
|
|
2127
|
+
row-gap: 24px;
|
|
2128
|
+
box-shadow: inset 0 0 0 1px var(--rs-color-black-t20);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
.buy-tile__first-half {
|
|
2132
|
+
grid-row: 1 / span 1;
|
|
2133
|
+
}
|
|
2134
|
+
.buy-tile__second-half {
|
|
2135
|
+
grid-row: 2 / span 1;
|
|
2136
|
+
|
|
2137
|
+
display: flex;
|
|
2138
|
+
flex-direction: column;
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
/* tile content styles */
|
|
2142
|
+
|
|
2143
|
+
.buy-tile-content__footer-wrapper {
|
|
2144
|
+
display: flex;
|
|
2145
|
+
flex: 1 0 auto;
|
|
2146
|
+
align-items: flex-end;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
.buy-tile-content__products {
|
|
2150
|
+
column-count: 2;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
.buy-tile-content__products-item {
|
|
2154
|
+
display: flex;
|
|
2155
|
+
column-gap: 8px;
|
|
2156
|
+
|
|
2157
|
+
align-items: center;
|
|
2158
|
+
|
|
2159
|
+
padding-top: 8px;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
.buy-tile-content__products-logo {
|
|
2163
|
+
width: 20px;
|
|
2164
|
+
height: 20px;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
.buy-tile-content__nowrap {
|
|
2168
|
+
white-space: nowrap;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
.buy-tile-content__text-right {
|
|
2172
|
+
text-align: right;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
.buy-tile-content__price-row {
|
|
2176
|
+
display: flex;
|
|
2177
|
+
justify-content: space-between;
|
|
2178
|
+
|
|
2179
|
+
&_baseline {
|
|
2180
|
+
align-items: baseline;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
.buy-tile-content__price-sup {
|
|
2185
|
+
vertical-align: super;
|
|
2186
|
+
|
|
2187
|
+
font-size: medium;
|
|
2188
|
+
font-weight: 400;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
.buy-tile-content__footer {
|
|
2192
|
+
display: flex;
|
|
2193
|
+
flex-wrap: wrap;
|
|
2194
|
+
justify-content: space-between;
|
|
2195
|
+
align-items: center;
|
|
2196
|
+
|
|
2197
|
+
width: 100%;
|
|
2198
|
+
column-gap: 32px;
|
|
2199
|
+
row-gap: 24px;
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
.buy-tile-content__links {
|
|
2203
|
+
display: flex;
|
|
2204
|
+
column-gap: 16px;
|
|
2205
|
+
row-gap: 12px;
|
|
2206
|
+
|
|
2207
|
+
flex-wrap: wrap;
|
|
2208
|
+
}
|
|
2209
|
+
```
|
|
2210
|
+
|
|
2211
|
+
### Alternative "buy" tile
|
|
2212
|
+
|
|
2213
|
+
##### Details:
|
|
2214
|
+
|
|
2215
|
+
- Border has a non-transparent color
|
|
2216
|
+
- Align content between sibling tiles using subgrids
|
|
2217
|
+
- Use empty grid rows to support large forms in particular tiles
|
|
2218
|
+
- Use margins instead of `row-gap` because of empty grid rows
|
|
2219
|
+
- Change primary colors using css variables
|
|
2220
|
+
- Use pseudo-element for border to show it above content
|
|
2221
|
+
|
|
2222
|
+
```tsx
|
|
2223
|
+
import { useState } from 'react';
|
|
2224
|
+
import cn from 'classnames';
|
|
2225
|
+
import { textCn } from '@rescui/typography';
|
|
2226
|
+
import { Tag, presets } from '@rescui/tag';
|
|
2227
|
+
import { Button } from '@rescui/button';
|
|
2228
|
+
import { Checkbox } from '@rescui/checkbox';
|
|
2229
|
+
import { DownOutlineIcon, UpOutlineIcon } from '@rescui/icons';
|
|
2230
|
+
import { RustroverLogo } from '@jetbrains/logos/react';
|
|
2231
|
+
import { Input } from '@rescui/input';
|
|
2232
|
+
|
|
2233
|
+
const FirstTile = () => {
|
|
2234
|
+
const [isFormOpened, setIsFormOpened] = useState(false);
|
|
2235
|
+
|
|
2236
|
+
const ButtonIcon = isFormOpened ? UpOutlineIcon : DownOutlineIcon;
|
|
2237
|
+
|
|
2238
|
+
return (
|
|
2239
|
+
<div
|
|
2240
|
+
className={cn(
|
|
2241
|
+
'alternative-buy-tile',
|
|
2242
|
+
isFormOpened && 'alternative-buy-tile_with-form'
|
|
2243
|
+
)}
|
|
2244
|
+
>
|
|
2245
|
+
<div className="alternative-buy-tile__header">
|
|
2246
|
+
<div className="alternative-buy-tile-content__flex-row">
|
|
2247
|
+
<h3 className={textCn('rs-h3')}>RustRover</h3>
|
|
2248
|
+
<Tag {...presets['filled-light']}>non-commercial use</Tag>
|
|
2249
|
+
</div>
|
|
2250
|
+
</div>
|
|
2251
|
+
<div className="alternative-buy-tile__price">
|
|
2252
|
+
<div className={textCn('rs-digits-2')}>Free</div>
|
|
2253
|
+
</div>
|
|
2254
|
+
<div className="alternative-buy-tile__get">
|
|
2255
|
+
<Button
|
|
2256
|
+
mode="outline"
|
|
2257
|
+
className="alternative-buy-tile-content__button"
|
|
2258
|
+
icon={<ButtonIcon />}
|
|
2259
|
+
onClick={() => setIsFormOpened(v => !v)}
|
|
2260
|
+
>
|
|
2261
|
+
Get
|
|
2262
|
+
</Button>
|
|
2263
|
+
{isFormOpened && (
|
|
2264
|
+
<div>
|
|
2265
|
+
<Input
|
|
2266
|
+
placeholder="First name"
|
|
2267
|
+
size="m"
|
|
2268
|
+
className="rs-docs-offset-top-16"
|
|
2269
|
+
/>
|
|
2270
|
+
<Input
|
|
2271
|
+
placeholder="Last name"
|
|
2272
|
+
size="m"
|
|
2273
|
+
className="rs-docs-offset-top-16"
|
|
2274
|
+
/>
|
|
2275
|
+
<Input
|
|
2276
|
+
placeholder="Email"
|
|
2277
|
+
type="email"
|
|
2278
|
+
size="m"
|
|
2279
|
+
className="rs-docs-offset-top-16"
|
|
2280
|
+
/>
|
|
2281
|
+
<Input
|
|
2282
|
+
placeholder="Select country"
|
|
2283
|
+
size="m"
|
|
2284
|
+
className="rs-docs-offset-top-16"
|
|
2285
|
+
/>
|
|
2286
|
+
<p className={cn(textCn('rs-text-3'), 'rs-docs-offset-top-16')}>
|
|
2287
|
+
JetBrains may ask you for clarification on whether the license is
|
|
2288
|
+
used solely for non-commercial purposes. In the event of any
|
|
2289
|
+
doubts, JetBrains reserves the right to revoke the license for
|
|
2290
|
+
non-compliance with the non-commercial use conditions.
|
|
2291
|
+
</p>
|
|
2292
|
+
<Checkbox size="s" className="rs-docs-offset-top-8">
|
|
2293
|
+
I agree to the Toolbox Subscription Agreement for Non-Commercial
|
|
2294
|
+
Use
|
|
2295
|
+
</Checkbox>
|
|
2296
|
+
<Button mode="rock" size="s" className="rs-docs-offset-top-16">
|
|
2297
|
+
Submit
|
|
2298
|
+
</Button>
|
|
2299
|
+
</div>
|
|
2300
|
+
)}
|
|
2301
|
+
</div>
|
|
2302
|
+
<div className="alternative-buy-tile__footer">
|
|
2303
|
+
<div className="alternative-buy-tile-content__flex-row">
|
|
2304
|
+
<RustroverLogo className="alternative-buy-tile-content__footer-logo" />
|
|
2305
|
+
<span className={textCn('rs-text-2')}>RustRover</span>
|
|
2306
|
+
</div>
|
|
2307
|
+
<p
|
|
2308
|
+
className={cn(
|
|
2309
|
+
textCn('rs-text-2', { hardness: 'hard' }),
|
|
2310
|
+
'rs-docs-offset-top-8'
|
|
2311
|
+
)}
|
|
2312
|
+
>
|
|
2313
|
+
A powerhouse IDE for Rust developers
|
|
2314
|
+
</p>
|
|
2315
|
+
<ul className={cn(textCn('rs-text-2'), textCn('rs-ul'))}>
|
|
2316
|
+
<li>Requires JetBrains Account</li>
|
|
2317
|
+
<li>Anonymous data is collected</li>
|
|
2318
|
+
</ul>
|
|
2319
|
+
</div>
|
|
2320
|
+
</div>
|
|
2321
|
+
);
|
|
2322
|
+
};
|
|
2323
|
+
|
|
2324
|
+
const SecondTile = () => {
|
|
2325
|
+
return (
|
|
2326
|
+
<div className="alternative-buy-tile">
|
|
2327
|
+
<div className="alternative-buy-tile__header">
|
|
2328
|
+
<h3 className={textCn('rs-h3')}>RustRover</h3>
|
|
2329
|
+
<Checkbox className="rs-docs-offset-top-24">
|
|
2330
|
+
Supercharge with{' '}
|
|
2331
|
+
<a href="#alternative-buy-tile" className={textCn('rs-link')}>
|
|
2332
|
+
JetBrains AI Pro
|
|
2333
|
+
</a>
|
|
2334
|
+
<p className={textCn('rs-text-2', { hardness: 'pale' })}>€100.00 (incl. VAT €119.00)</p>
|
|
2335
|
+
</Checkbox>
|
|
2336
|
+
</div>
|
|
2337
|
+
<div className="alternative-buy-tile__price">
|
|
2338
|
+
<div className="alternative-buy-tile-content__price-row">
|
|
2339
|
+
<div
|
|
2340
|
+
className={cn(
|
|
2341
|
+
'alternative-buy-tile-content__nowrap',
|
|
2342
|
+
textCn('rs-digits-2')
|
|
2343
|
+
)}
|
|
2344
|
+
>
|
|
2345
|
+
€69
|
|
2346
|
+
<sup className="alternative-buy-tile-content__price-sup">.00</sup>
|
|
2347
|
+
</div>
|
|
2348
|
+
<span className={textCn('rs-text-3')}>a year</span>
|
|
2349
|
+
</div>
|
|
2350
|
+
<p className={textCn('rs-text-3')}>incl. VAT €82.11</p>
|
|
2351
|
+
</div>
|
|
2352
|
+
<div className="alternative-buy-tile__get">
|
|
2353
|
+
<Button mode="classic" className="alternative-buy-tile-content__button">
|
|
2354
|
+
Buy
|
|
2355
|
+
</Button>
|
|
2356
|
+
<p
|
|
2357
|
+
className={cn(
|
|
2358
|
+
textCn('rs-text-2'),
|
|
2359
|
+
'rs-docs-offset-top-12',
|
|
2360
|
+
'alternative-buy-tile-content__text-center'
|
|
2361
|
+
)}
|
|
2362
|
+
>
|
|
2363
|
+
<a
|
|
2364
|
+
href="#alternative-buy-tile"
|
|
2365
|
+
className={textCn('rs-link', { mode: 'standalone' })}
|
|
2366
|
+
>
|
|
2367
|
+
Get quote
|
|
2368
|
+
</a>
|
|
2369
|
+
</p>
|
|
2370
|
+
</div>
|
|
2371
|
+
<div className="alternative-buy-tile__footer">
|
|
2372
|
+
<div className="alternative-buy-tile-content__flex-row">
|
|
2373
|
+
<RustroverLogo className="alternative-buy-tile-content__footer-logo" />
|
|
2374
|
+
<span className={textCn('rs-text-2')}>RustRover</span>
|
|
2375
|
+
</div>
|
|
2376
|
+
<p
|
|
2377
|
+
className={cn(
|
|
2378
|
+
textCn('rs-text-2', { hardness: 'hard' }),
|
|
2379
|
+
'rs-docs-offset-top-8'
|
|
2380
|
+
)}
|
|
2381
|
+
>
|
|
2382
|
+
A powerhouse IDE for Rust developers
|
|
2383
|
+
</p>
|
|
2384
|
+
<ul className={cn(textCn('rs-text-2'), textCn('rs-ul'))}>
|
|
2385
|
+
<li>
|
|
2386
|
+
Activated with a JetBrains Account, offline key, or license server
|
|
2387
|
+
</li>
|
|
2388
|
+
<li>Fine-grained control over shared data</li>
|
|
2389
|
+
</ul>
|
|
2390
|
+
</div>
|
|
2391
|
+
</div>
|
|
2392
|
+
);
|
|
2393
|
+
};
|
|
2394
|
+
|
|
2395
|
+
const ThirdTile = () => {
|
|
2396
|
+
return (
|
|
2397
|
+
<div className="alternative-buy-tile">
|
|
2398
|
+
<div className="alternative-buy-tile__header">
|
|
2399
|
+
<h3 className={textCn('rs-h3')}>Rust plugin for CLion</h3>
|
|
2400
|
+
</div>
|
|
2401
|
+
<div className="alternative-buy-tile__price">
|
|
2402
|
+
<div className="alternative-buy-tile-content__price-row">
|
|
2403
|
+
<div
|
|
2404
|
+
className={cn(
|
|
2405
|
+
'alternative-buy-tile-content__nowrap',
|
|
2406
|
+
textCn('rs-digits-2')
|
|
2407
|
+
)}
|
|
2408
|
+
>
|
|
2409
|
+
€29
|
|
2410
|
+
<sup className="alternative-buy-tile-content__price-sup">.00</sup>
|
|
2411
|
+
</div>
|
|
2412
|
+
<span className={textCn('rs-text-3')}>a year</span>
|
|
2413
|
+
</div>
|
|
2414
|
+
<p className={textCn('rs-text-3')}>incl. VAT €34.51</p>
|
|
2415
|
+
</div>
|
|
2416
|
+
<div className="alternative-buy-tile__get">
|
|
2417
|
+
<Button mode="classic" className="alternative-buy-tile-content__button">
|
|
2418
|
+
Buy
|
|
2419
|
+
</Button>
|
|
2420
|
+
<p
|
|
2421
|
+
className={cn(
|
|
2422
|
+
textCn('rs-text-2'),
|
|
2423
|
+
'rs-docs-offset-top-12',
|
|
2424
|
+
'alternative-buy-tile-content__text-center'
|
|
2425
|
+
)}
|
|
2426
|
+
>
|
|
2427
|
+
<a
|
|
2428
|
+
href="#alternative-buy-tile"
|
|
2429
|
+
className={textCn('rs-link', { mode: 'standalone' })}
|
|
2430
|
+
>
|
|
2431
|
+
Get quote
|
|
2432
|
+
</a>
|
|
2433
|
+
</p>
|
|
2434
|
+
</div>
|
|
2435
|
+
<div className="alternative-buy-tile__footer">
|
|
2436
|
+
<div className="alternative-buy-tile-content__flex-row">
|
|
2437
|
+
<RustroverLogo className="alternative-buy-tile-content__footer-logo" />
|
|
2438
|
+
<span className={textCn('rs-text-2')}>RustRover</span>
|
|
2439
|
+
</div>
|
|
2440
|
+
<p
|
|
2441
|
+
className={cn(
|
|
2442
|
+
textCn('rs-text-2', { hardness: 'hard' }),
|
|
2443
|
+
'rs-docs-offset-top-8'
|
|
2444
|
+
)}
|
|
2445
|
+
>
|
|
2446
|
+
The plugin provides Rust language support
|
|
2447
|
+
</p>
|
|
2448
|
+
<ul className={cn(textCn('rs-text-2'), textCn('rs-ul'))}>
|
|
2449
|
+
<li>30-day trial available</li>
|
|
2450
|
+
</ul>
|
|
2451
|
+
</div>
|
|
2452
|
+
</div>
|
|
2453
|
+
);
|
|
2454
|
+
};
|
|
2455
|
+
|
|
2456
|
+
const Demo = () => {
|
|
2457
|
+
return (
|
|
2458
|
+
<div className="alternative-buy-tiles-container">
|
|
2459
|
+
<div className="alternative-buy-tiles-container__tile-wrapper">
|
|
2460
|
+
<FirstTile />
|
|
2461
|
+
</div>
|
|
2462
|
+
<div className="alternative-buy-tiles-container__tile-wrapper">
|
|
2463
|
+
<SecondTile />
|
|
2464
|
+
</div>
|
|
2465
|
+
<div className="alternative-buy-tiles-container__tile-wrapper">
|
|
2466
|
+
<ThirdTile />
|
|
2467
|
+
</div>
|
|
2468
|
+
<div className="alternative-buy-tiles-container__tile-wrapper">
|
|
2469
|
+
<ThirdTile />
|
|
2470
|
+
</div>
|
|
2471
|
+
</div>
|
|
2472
|
+
);
|
|
2473
|
+
};
|
|
2474
|
+
|
|
2475
|
+
render(<Demo />);
|
|
2476
|
+
```
|
|
2477
|
+
|
|
2478
|
+
```postcss
|
|
2479
|
+
/* container styles */
|
|
2480
|
+
|
|
2481
|
+
.alternative-buy-tiles-container {
|
|
2482
|
+
--rs-color-primary-light-theme: #00874d;
|
|
2483
|
+
--rs-color-primary-dim-light-theme: #cce7db;
|
|
2484
|
+
--rs-color-primary-fog-light-theme: #339f71;
|
|
2485
|
+
--rs-color-primary-t-dim-light-theme: rgba(0, 135, 77, 0.8);
|
|
2486
|
+
--rs-color-primary-t-fog-light-theme: rgba(0, 135, 77, 0.2);
|
|
2487
|
+
--rs-color-primary-dark-theme: #00874d;
|
|
2488
|
+
--rs-color-primary-dim-dark-theme: #142f26;
|
|
2489
|
+
--rs-color-primary-fog-dark-theme: #057143;
|
|
2490
|
+
--rs-color-primary-t-dim-dark-theme: rgba(0, 135, 77, 0.8);
|
|
2491
|
+
--rs-color-primary-t-fog-dark-theme: rgba(0, 135, 77, 0.2);
|
|
2492
|
+
|
|
2493
|
+
display: grid;
|
|
2494
|
+
grid-template-columns: repeat(3, 1fr);
|
|
2495
|
+
grid-template-rows: auto;
|
|
2496
|
+
|
|
2497
|
+
column-gap: 32px;
|
|
2498
|
+
row-gap: 0;
|
|
2499
|
+
|
|
2500
|
+
margin-top: -32px;
|
|
2501
|
+
|
|
2502
|
+
@media (--md) {
|
|
2503
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
@media (--sm) {
|
|
2507
|
+
grid-template-columns: 1fr;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
.alternative-buy-tiles-container__tile-wrapper {
|
|
2512
|
+
display: grid;
|
|
2513
|
+
grid-template-columns: 1fr;
|
|
2514
|
+
grid-template-rows: subgrid;
|
|
2515
|
+
grid-row: auto / span 6;
|
|
2516
|
+
|
|
2517
|
+
margin-top: 32px;
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
/* tile styles */
|
|
2521
|
+
|
|
2522
|
+
.alternative-buy-tile {
|
|
2523
|
+
--my-tile-padding: 24px;
|
|
2524
|
+
|
|
2525
|
+
position: relative;
|
|
2526
|
+
|
|
2527
|
+
display: grid;
|
|
2528
|
+
overflow: hidden;
|
|
2529
|
+
|
|
2530
|
+
box-sizing: border-box;
|
|
2531
|
+
min-height: 0;
|
|
2532
|
+
|
|
2533
|
+
padding: var(--my-tile-padding);
|
|
2534
|
+
|
|
2535
|
+
border-radius: 8px;
|
|
2536
|
+
|
|
2537
|
+
background-color: transparent;
|
|
2538
|
+
grid-template-columns: 1fr;
|
|
2539
|
+
grid-template-rows: subgrid;
|
|
2540
|
+
grid-row: auto / span 4;
|
|
2541
|
+
|
|
2542
|
+
&_with-form {
|
|
2543
|
+
grid-row: auto / span 6;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
&::after {
|
|
2547
|
+
position: absolute;
|
|
2548
|
+
top: 0;
|
|
2549
|
+
right: 0;
|
|
2550
|
+
bottom: 0;
|
|
2551
|
+
left: 0;
|
|
2552
|
+
|
|
2553
|
+
display: block;
|
|
2554
|
+
|
|
2555
|
+
border-radius: inherit;
|
|
2556
|
+
|
|
2557
|
+
content: '';
|
|
2558
|
+
pointer-events: none;
|
|
2559
|
+
|
|
2560
|
+
box-shadow: inset 0 0 0 1px var(--rs-color-grey-20);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
.alternative-buy-tile__header {
|
|
2565
|
+
grid-row: 1;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
.alternative-buy-tile__price {
|
|
2569
|
+
grid-row: 2;
|
|
2570
|
+
|
|
2571
|
+
margin-top: 16px;
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
.alternative-buy-tile__get {
|
|
2575
|
+
margin-top: 24px;
|
|
2576
|
+
margin-bottom: 24px;
|
|
2577
|
+
|
|
2578
|
+
grid-row: 3;
|
|
2579
|
+
|
|
2580
|
+
.alternative-buy-tile_with-form & {
|
|
2581
|
+
grid-row: 3 / span 3;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
.alternative-buy-tile__footer {
|
|
2586
|
+
grid-row: 4;
|
|
2587
|
+
|
|
2588
|
+
margin: 0 calc(-1 * var(--my-tile-padding)) calc(-1 * var(--my-tile-padding));
|
|
2589
|
+
padding: var(--my-tile-padding);
|
|
2590
|
+
|
|
2591
|
+
background: var(--rs-color-grey-5);
|
|
2592
|
+
|
|
2593
|
+
.alternative-buy-tile_with-form & {
|
|
2594
|
+
grid-row: 6;
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
/* tile content styles */
|
|
2599
|
+
|
|
2600
|
+
.alternative-buy-tile-content__flex-row {
|
|
2601
|
+
display: flex;
|
|
2602
|
+
flex-wrap: wrap;
|
|
2603
|
+
column-gap: 8px;
|
|
2604
|
+
row-gap: 8px;
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
.alternative-buy-tile-content__footer-logo {
|
|
2608
|
+
height: 24px;
|
|
2609
|
+
width: auto;
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
.alternative-buy-tile-content__button {
|
|
2613
|
+
width: 100%;
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
.alternative-buy-tile-content__text-center {
|
|
2617
|
+
text-align: center;
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
.alternative-buy-tile-content__nowrap {
|
|
2621
|
+
white-space: nowrap;
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
.alternative-buy-tile-content__price-row {
|
|
2625
|
+
display: flex;
|
|
2626
|
+
column-gap: 8px;
|
|
2627
|
+
|
|
2628
|
+
align-items: baseline;
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
.alternative-buy-tile-content__price-sup {
|
|
2632
|
+
vertical-align: super;
|
|
2633
|
+
|
|
2634
|
+
font-size: medium;
|
|
2635
|
+
font-weight: 400;
|
|
2636
|
+
}
|
|
2637
|
+
```
|
|
2638
|
+
|
|
2639
|
+
## Controls
|
|
2640
|
+
|
|
2641
|
+
### Carousel vertical controls
|
|
2642
|
+
|
|
2643
|
+
##### Details:
|
|
2644
|
+
|
|
2645
|
+
- Without border
|
|
2646
|
+
- With `selected` state
|
|
2647
|
+
|
|
2648
|
+
- With filling progress bar
|
|
2649
|
+
- With hover and focus-visible states
|
|
2650
|
+
- Control autoplay animation duration with `--carousel-vertical-control-autoplay-duration` css variable
|
|
2651
|
+
|
|
2652
|
+
- With react state management
|
|
2653
|
+
|
|
2654
|
+
```tsx
|
|
2655
|
+
import { useState } from 'react';
|
|
2656
|
+
import cn from 'classnames';
|
|
2657
|
+
import { textCn } from '@rescui/typography';
|
|
2658
|
+
|
|
2659
|
+
const slidesTitles = [
|
|
2660
|
+
'First slide',
|
|
2661
|
+
'Second slide',
|
|
2662
|
+
'Third slide',
|
|
2663
|
+
'Fourth slide'
|
|
2664
|
+
];
|
|
2665
|
+
|
|
2666
|
+
const AUTOPLAY_DURATION = '8s';
|
|
2667
|
+
|
|
2668
|
+
const Demo = () => {
|
|
2669
|
+
const [currentItem, setCurrentItem] = useState(0);
|
|
2670
|
+
const [isAutoplayEnabled, setIsAutoplayEnabled] = useState(true);
|
|
2671
|
+
|
|
2672
|
+
return (
|
|
2673
|
+
<ul
|
|
2674
|
+
className={cn(
|
|
2675
|
+
'carousel-vertical-controls',
|
|
2676
|
+
isAutoplayEnabled && 'carousel-vertical-controls_autoplay'
|
|
2677
|
+
)}
|
|
2678
|
+
style={{
|
|
2679
|
+
'--carousel-vertical-control-autoplay-duration': AUTOPLAY_DURATION
|
|
2680
|
+
}}
|
|
2681
|
+
>
|
|
2682
|
+
{slidesTitles.map((title, i) => (
|
|
2683
|
+
<li key={i}>
|
|
2684
|
+
<button
|
|
2685
|
+
type="button"
|
|
2686
|
+
className={cn(
|
|
2687
|
+
'carousel-vertical-controls__item',
|
|
2688
|
+
i === currentItem && 'carousel-vertical-controls__item_selected'
|
|
2689
|
+
)}
|
|
2690
|
+
onAnimationEnd={() => {
|
|
2691
|
+
setCurrentItem(curVal =>
|
|
2692
|
+
curVal >= slidesTitles.length - 1 ? 0 : curVal + 1
|
|
2693
|
+
);
|
|
2694
|
+
}}
|
|
2695
|
+
onClick={() => {
|
|
2696
|
+
setCurrentItem(i);
|
|
2697
|
+
setIsAutoplayEnabled(false);
|
|
2698
|
+
}}
|
|
2699
|
+
>
|
|
2700
|
+
<p className={'carousel-vertical-controls__item-title'}>{title}</p>
|
|
2701
|
+
<div className={'carousel-vertical-controls__item-content'}>
|
|
2702
|
+
<p className={textCn('rs-text-2', { hardness: 'hard' })}>
|
|
2703
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab
|
|
2704
|
+
aliquid asperiores aut earum fuga in incidunt ipsam maiores
|
|
2705
|
+
officia possimus quo repudiandae sequi similique temporibus.
|
|
2706
|
+
</p>
|
|
2707
|
+
</div>
|
|
2708
|
+
</button>
|
|
2709
|
+
</li>
|
|
2710
|
+
))}
|
|
2711
|
+
</ul>
|
|
2712
|
+
);
|
|
2713
|
+
};
|
|
2714
|
+
|
|
2715
|
+
render(<Demo />);
|
|
2716
|
+
```
|
|
2717
|
+
|
|
2718
|
+
```postcss attached
|
|
2719
|
+
@import '@rescui/typography/lib/public-api.p.css';
|
|
2720
|
+
|
|
2721
|
+
@keyframes carousel-vertical-controls-progress-bar-fill {
|
|
2722
|
+
0% {
|
|
2723
|
+
transform: translateX(-100%);
|
|
2724
|
+
}
|
|
2725
|
+
100% {
|
|
2726
|
+
transform: translateX(0%);
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
.carousel-vertical-controls {
|
|
2731
|
+
display: flex;
|
|
2732
|
+
flex-direction: column;
|
|
2733
|
+
row-gap: 8px;
|
|
2734
|
+
|
|
2735
|
+
width: 350px;
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
.carousel-vertical-controls__item-title {
|
|
2739
|
+
@mixin rs-typography-text-2-full;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
.carousel-vertical-controls__item-content {
|
|
2743
|
+
display: none;
|
|
2744
|
+
|
|
2745
|
+
margin-top: 12px;
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
.carousel-vertical-controls__item {
|
|
2749
|
+
position: relative;
|
|
2750
|
+
|
|
2751
|
+
display: block;
|
|
2752
|
+
|
|
2753
|
+
overflow: hidden;
|
|
2754
|
+
|
|
2755
|
+
box-sizing: border-box;
|
|
2756
|
+
|
|
2757
|
+
width: 100%;
|
|
2758
|
+
min-height: 0;
|
|
2759
|
+
padding: 12px 24px;
|
|
2760
|
+
|
|
2761
|
+
border: none;
|
|
2762
|
+
border-radius: 24px;
|
|
2763
|
+
|
|
2764
|
+
cursor: pointer;
|
|
2765
|
+
text-align: inherit;
|
|
2766
|
+
|
|
2767
|
+
background-color: transparent;
|
|
2768
|
+
|
|
2769
|
+
appearance: none;
|
|
2770
|
+
|
|
2771
|
+
&,
|
|
2772
|
+
&:hover {
|
|
2773
|
+
text-decoration: none;
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
/* reset user agent stylesheet at Chrome */
|
|
2777
|
+
&:focus-visible {
|
|
2778
|
+
outline: none;
|
|
2779
|
+
|
|
2780
|
+
box-shadow: 0 0 0 4px var(--rs-color-primary-dim-light-theme);
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2783
|
+
@media (hover: hover) {
|
|
2784
|
+
&:hover {
|
|
2785
|
+
& .carousel-vertical-controls__item-title {
|
|
2786
|
+
@mixin rs-typography-hardness-hard;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
&:active {
|
|
2792
|
+
& .carousel-vertical-controls__item-title {
|
|
2793
|
+
@mixin rs-typography-hardness-hard;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
.carousel-vertical-controls__item_selected {
|
|
2799
|
+
--rs-theme-dark: 1;
|
|
2800
|
+
|
|
2801
|
+
overflow: hidden;
|
|
2802
|
+
|
|
2803
|
+
padding: 24px;
|
|
2804
|
+
|
|
2805
|
+
background-color: #5848cf;
|
|
2806
|
+
|
|
2807
|
+
& > * {
|
|
2808
|
+
position: relative;
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2811
|
+
& .carousel-vertical-controls__item-title {
|
|
2812
|
+
@mixin rs-typography-h4;
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
& .carousel-vertical-controls__item-content {
|
|
2816
|
+
display: block;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
.carousel-vertical-controls_autoplay &::before {
|
|
2820
|
+
position: absolute;
|
|
2821
|
+
top: 0;
|
|
2822
|
+
right: 0;
|
|
2823
|
+
bottom: 0;
|
|
2824
|
+
left: 0;
|
|
2825
|
+
|
|
2826
|
+
display: block;
|
|
2827
|
+
|
|
2828
|
+
border-radius: 0;
|
|
2829
|
+
|
|
2830
|
+
content: '';
|
|
2831
|
+
pointer-events: none;
|
|
2832
|
+
|
|
2833
|
+
background-color: var(--rs-color-primary-light-theme);
|
|
2834
|
+
|
|
2835
|
+
animation: var(--carousel-vertical-control-autoplay-duration, 8s) linear both
|
|
2836
|
+
carousel-vertical-controls-progress-bar-fill;
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
```
|
|
2840
|
+
|
|
2841
|
+
### Carousel horizontal controls
|
|
2842
|
+
|
|
2843
|
+
##### Details:
|
|
2844
|
+
|
|
2845
|
+
- Without border
|
|
2846
|
+
- With `selected` state
|
|
2847
|
+
|
|
2848
|
+
- With filling progress bar
|
|
2849
|
+
- With hover and focus-visible states
|
|
2850
|
+
- Control autoplay animation duration with `--carousel-horizontal-control-autoplay-duration` css variable
|
|
2851
|
+
|
|
2852
|
+
- With react state management
|
|
2853
|
+
|
|
2854
|
+
```tsx
|
|
2855
|
+
import { useState } from 'react';
|
|
2856
|
+
import cn from 'classnames';
|
|
2857
|
+
import { textCn } from '@rescui/typography';
|
|
2858
|
+
|
|
2859
|
+
const slidesTitles = [
|
|
2860
|
+
'First slide',
|
|
2861
|
+
'Second slide',
|
|
2862
|
+
'Third slide',
|
|
2863
|
+
'Fourth slide'
|
|
2864
|
+
];
|
|
2865
|
+
|
|
2866
|
+
const AUTOPLAY_DURATION = '8s';
|
|
2867
|
+
|
|
2868
|
+
const Demo = () => {
|
|
2869
|
+
const [currentItem, setCurrentItem] = useState(0);
|
|
2870
|
+
const [isAutoplayEnabled, setIsAutoplayEnabled] = useState(true);
|
|
2871
|
+
|
|
2872
|
+
return (
|
|
2873
|
+
<ul
|
|
2874
|
+
className={cn(
|
|
2875
|
+
'carousel-horizontal-controls',
|
|
2876
|
+
isAutoplayEnabled && 'carousel-horizontal-controls_autoplay',
|
|
2877
|
+
'jb-offset-top-32'
|
|
2878
|
+
)}
|
|
2879
|
+
style={{
|
|
2880
|
+
'--carousel-vertical-control-autoplay-duration': AUTOPLAY_DURATION
|
|
2881
|
+
}}
|
|
2882
|
+
>
|
|
2883
|
+
{slidesTitles.map((title, i) => (
|
|
2884
|
+
<li key={i}>
|
|
2885
|
+
<button
|
|
2886
|
+
type="button"
|
|
2887
|
+
className={cn(
|
|
2888
|
+
'carousel-horizontal-controls__item',
|
|
2889
|
+
i === currentItem && 'carousel-horizontal-controls__item_selected'
|
|
2890
|
+
)}
|
|
2891
|
+
onAnimationEnd={() => {
|
|
2892
|
+
setCurrentItem(curVal =>
|
|
2893
|
+
curVal >= slidesTitles.length - 1 ? 0 : curVal + 1
|
|
2894
|
+
);
|
|
2895
|
+
}}
|
|
2896
|
+
onClick={() => {
|
|
2897
|
+
setCurrentItem(i);
|
|
2898
|
+
setIsAutoplayEnabled(false);
|
|
2899
|
+
}}
|
|
2900
|
+
>
|
|
2901
|
+
<p
|
|
2902
|
+
className={cn(
|
|
2903
|
+
textCn('rs-text-2'),
|
|
2904
|
+
'carousel-horizontal-controls__item-title'
|
|
2905
|
+
)}
|
|
2906
|
+
>
|
|
2907
|
+
{title}
|
|
2908
|
+
</p>
|
|
2909
|
+
</button>
|
|
2910
|
+
</li>
|
|
2911
|
+
))}
|
|
2912
|
+
</ul>
|
|
2913
|
+
);
|
|
2914
|
+
};
|
|
2915
|
+
|
|
2916
|
+
render(<Demo />);
|
|
2917
|
+
```
|
|
2918
|
+
|
|
2919
|
+
```postcss
|
|
2920
|
+
@import '@rescui/typography/lib/public-api.p.css';
|
|
2921
|
+
|
|
2922
|
+
@keyframes carousel-horizontal-controls-progress-bar-fill {
|
|
2923
|
+
0% {
|
|
2924
|
+
transform: translateX(-100%);
|
|
2925
|
+
}
|
|
2926
|
+
80% {
|
|
2927
|
+
transform: translateX(0%);
|
|
2928
|
+
}
|
|
2929
|
+
100% {
|
|
2930
|
+
transform: translateX(0%);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
.carousel-horizontal-controls {
|
|
2935
|
+
display: flex;
|
|
2936
|
+
column-gap: 8px;
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
.carousel-horizontal-controls__item {
|
|
2940
|
+
--rs-theme-dark: 1;
|
|
2941
|
+
position: relative;
|
|
2942
|
+
|
|
2943
|
+
display: block;
|
|
2944
|
+
|
|
2945
|
+
overflow: hidden;
|
|
2946
|
+
|
|
2947
|
+
box-sizing: border-box;
|
|
2948
|
+
|
|
2949
|
+
min-height: 0;
|
|
2950
|
+
padding: 24px;
|
|
2951
|
+
|
|
2952
|
+
border: none;
|
|
2953
|
+
border-radius: 24px;
|
|
2954
|
+
|
|
2955
|
+
cursor: pointer;
|
|
2956
|
+
text-align: inherit;
|
|
2957
|
+
|
|
2958
|
+
background-color: var(--rs-color-black-t80);
|
|
2959
|
+
|
|
2960
|
+
appearance: none;
|
|
2961
|
+
|
|
2962
|
+
&,
|
|
2963
|
+
&:hover {
|
|
2964
|
+
text-decoration: none;
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
/* reset user agent stylesheet at Chrome */
|
|
2968
|
+
&:focus-visible {
|
|
2969
|
+
outline: none;
|
|
2970
|
+
|
|
2971
|
+
box-shadow: 0 0 0 4px var(--rs-color-primary-dim-light-theme);
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
@media (hover: hover) {
|
|
2975
|
+
&:hover {
|
|
2976
|
+
& .carousel-horizontal-controls__item-title {
|
|
2977
|
+
@mixin rs-typography-hardness-hard;
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
&:active {
|
|
2983
|
+
& .carousel-horizontal-controls__item-title {
|
|
2984
|
+
@mixin rs-typography-hardness-hard;
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
.carousel-horizontal-controls__item_selected {
|
|
2990
|
+
overflow: hidden;
|
|
2991
|
+
|
|
2992
|
+
background-color: var(--rs-color-black-t90);
|
|
2993
|
+
|
|
2994
|
+
& .carousel-horizontal-controls__item-title {
|
|
2995
|
+
@mixin rs-typography-hardness-hard;
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
& > * {
|
|
2999
|
+
position: relative;
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
.carousel-horizontal-controls_autoplay &::before {
|
|
3003
|
+
position: absolute;
|
|
3004
|
+
top: 0;
|
|
3005
|
+
right: 0;
|
|
3006
|
+
bottom: 0;
|
|
3007
|
+
left: 0;
|
|
3008
|
+
|
|
3009
|
+
display: block;
|
|
3010
|
+
|
|
3011
|
+
border-radius: 0;
|
|
3012
|
+
|
|
3013
|
+
content: '';
|
|
3014
|
+
pointer-events: none;
|
|
3015
|
+
|
|
3016
|
+
background-color: var(--rs-color-primary-light-theme);
|
|
3017
|
+
|
|
3018
|
+
animation: var(--carousel-horizontal-control-autoplay-duration, 8s) linear both
|
|
3019
|
+
carousel-vertical-controls-progress-bar-fill;
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
```
|
|
3023
|
+
|
|
3024
|
+
## Guide
|
|
3025
|
+
|
|
3026
|
+
### Description
|
|
3027
|
+
|
|
3028
|
+
This guide provides a detailed explanation of how to create various types of cards from scratch.
|
|
3029
|
+
|
|
3030
|
+
The process is not strictly linear, so you can skip steps that are currently not relevant to you.
|
|
3031
|
+
|
|
3032
|
+
Additionally, this guide includes complete examples for the following types of cards:
|
|
3033
|
+
|
|
3034
|
+
- [Clickable cards](/cards/clickable/)
|
|
3035
|
+
- [Tiles](/cards/tiles/)
|
|
3036
|
+
- [Controls](/cards/controls/)
|
|
3037
|
+
|
|
3038
|
+
### Clickable cards
|
|
3039
|
+
|
|
3040
|
+
#### 1\. Template
|
|
3041
|
+
|
|
3042
|
+
Clickable cards should always be implemented using either a `<button>` or an `<a>` element. This requires manually resetting browser user-agent styles. Use the following CSS styles to create the foundation:
|
|
3043
|
+
|
|
3044
|
+
```postcss
|
|
3045
|
+
.my-card {
|
|
3046
|
+
position: relative;
|
|
3047
|
+
|
|
3048
|
+
display: block;
|
|
3049
|
+
|
|
3050
|
+
overflow: hidden;
|
|
3051
|
+
|
|
3052
|
+
box-sizing: border-box;
|
|
3053
|
+
min-height: 0;
|
|
3054
|
+
|
|
3055
|
+
border: none;
|
|
3056
|
+
border-radius: 0;
|
|
3057
|
+
|
|
3058
|
+
cursor: pointer;
|
|
3059
|
+
text-align: inherit;
|
|
3060
|
+
|
|
3061
|
+
background-color: var(--rs-color-white);
|
|
3062
|
+
|
|
3063
|
+
appearance: none;
|
|
3064
|
+
padding: 0;
|
|
3065
|
+
|
|
3066
|
+
&,
|
|
3067
|
+
&:hover {
|
|
3068
|
+
text-decoration: none;
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
&:focus-visible {
|
|
3072
|
+
outline: none;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
```
|
|
3076
|
+
|
|
3077
|
+
Some styles may require further updates in the next steps.
|
|
3078
|
+
|
|
3079
|
+
#### 2\. Add paddings and `border-radius`
|
|
3080
|
+
|
|
3081
|
+
In this step, update the padding and `border-radius` for your card:
|
|
3082
|
+
|
|
3083
|
+
```postcss
|
|
3084
|
+
.my-card {
|
|
3085
|
+
/* ... */
|
|
3086
|
+
|
|
3087
|
+
border-radius: 8px;
|
|
3088
|
+
padding: 24px;
|
|
3089
|
+
|
|
3090
|
+
/* ... */
|
|
3091
|
+
}
|
|
3092
|
+
```
|
|
3093
|
+
|
|
3094
|
+
#### 3\. Add a border
|
|
3095
|
+
|
|
3096
|
+
Due to [browser incompatibility issues with `border` styles in scaled UI](https://stackoverflow.com/questions/46105681/get-wrong-border-width-on-firefox), it is best to avoid the `border` property if you plan to dynamically change its width. Instead, use the `box-shadow` property for better consistency.
|
|
3097
|
+
|
|
3098
|
+
Alternatively, you can use a `::before` pseudo-element for the border, but using `box-shadow` is recommended whenever possible.
|
|
3099
|
+
|
|
3100
|
+
Here’s how to add a border using the `box-shadow` property:
|
|
3101
|
+
|
|
3102
|
+
```postcss
|
|
3103
|
+
.my-card {
|
|
3104
|
+
/* ... */
|
|
3105
|
+
|
|
3106
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
3107
|
+
--my-card-border-color
|
|
3108
|
+
);
|
|
3109
|
+
transition: box-shadow 100ms;
|
|
3110
|
+
|
|
3111
|
+
/* ... */
|
|
3112
|
+
}
|
|
3113
|
+
```
|
|
3114
|
+
|
|
3115
|
+
Next step, we add card state changes for the border. To make styling for various card states easier to manage, utilize CSS variables for border width and color:
|
|
3116
|
+
|
|
3117
|
+
```postcss
|
|
3118
|
+
.my-card {
|
|
3119
|
+
--my-card-border-width: 1px;
|
|
3120
|
+
--my-card-border-color: var(--rs-color-black-t20);
|
|
3121
|
+
|
|
3122
|
+
@media (hover: hover) {
|
|
3123
|
+
&:hover {
|
|
3124
|
+
--my-card-border-color: var(--rs-color-black);
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3128
|
+
&:focus-visible,
|
|
3129
|
+
&:active {
|
|
3130
|
+
--my-card-border-color: var(--rs-color-black);
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
&:active {
|
|
3134
|
+
--my-card-border-width: 2px;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
```
|
|
3138
|
+
|
|
3139
|
+
By combining these updates with previous steps, the complete code will look like this:
|
|
3140
|
+
|
|
3141
|
+
```postcss
|
|
3142
|
+
.my-card {
|
|
3143
|
+
--my-card-border-width: 1px;
|
|
3144
|
+
--my-card-border-color: var(--rs-color-black-t20);
|
|
3145
|
+
|
|
3146
|
+
position: relative;
|
|
3147
|
+
|
|
3148
|
+
display: block;
|
|
3149
|
+
|
|
3150
|
+
overflow: hidden;
|
|
3151
|
+
|
|
3152
|
+
box-sizing: border-box;
|
|
3153
|
+
min-height: 0;
|
|
3154
|
+
padding: 24px;
|
|
3155
|
+
|
|
3156
|
+
border: none;
|
|
3157
|
+
border-radius: 8px;
|
|
3158
|
+
|
|
3159
|
+
cursor: pointer;
|
|
3160
|
+
text-align: inherit;
|
|
3161
|
+
|
|
3162
|
+
background-color: var(--rs-color-white);
|
|
3163
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
3164
|
+
--my-card-border-color
|
|
3165
|
+
);
|
|
3166
|
+
|
|
3167
|
+
transition: box-shadow 100ms;
|
|
3168
|
+
appearance: none;
|
|
3169
|
+
|
|
3170
|
+
&,
|
|
3171
|
+
&:hover {
|
|
3172
|
+
text-decoration: none;
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
&:focus-visible {
|
|
3176
|
+
outline: none;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
@media (hover: hover) {
|
|
3180
|
+
&:hover {
|
|
3181
|
+
--my-card-border-color: var(--rs-color-black);
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
&:focus-visible,
|
|
3186
|
+
&:active {
|
|
3187
|
+
--my-card-border-color: var(--rs-color-black);
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
&:active {
|
|
3191
|
+
--my-card-border-width: 2px;
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
```
|
|
3195
|
+
|
|
3196
|
+
##### 3.1 Animate border color only
|
|
3197
|
+
|
|
3198
|
+
If you want only to animate border color, you can utilise `@property` at-rule. But keep in mind, it works only for [newer browsers](https://developer.mozilla.org/en-US/docs/Web/CSS/@property#browser_compatibility):
|
|
3199
|
+
|
|
3200
|
+
```postcss
|
|
3201
|
+
@property --my-card-border-color {
|
|
3202
|
+
syntax: '<color>';
|
|
3203
|
+
inherits: true;
|
|
3204
|
+
initial-value: transparent;
|
|
3205
|
+
}
|
|
3206
|
+
|
|
3207
|
+
.my-card {
|
|
3208
|
+
/* ... */
|
|
3209
|
+
|
|
3210
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
3211
|
+
--my-card-border-color
|
|
3212
|
+
);
|
|
3213
|
+
transition: --my-card-border-color 100ms;
|
|
3214
|
+
|
|
3215
|
+
/* ... */
|
|
3216
|
+
}
|
|
3217
|
+
```
|
|
3218
|
+
|
|
3219
|
+
##### 3.2 Border as a pseudo-element
|
|
3220
|
+
|
|
3221
|
+
When parts of the card, such as large header images, need to stretch fully to the edges, the `box-shadow` border may not render correctly. To fix this, you can use a `::after` pseudo-element to place the border:
|
|
3222
|
+
|
|
3223
|
+
```postcss
|
|
3224
|
+
.my-card {
|
|
3225
|
+
/* ... */
|
|
3226
|
+
|
|
3227
|
+
&::after {
|
|
3228
|
+
position: absolute;
|
|
3229
|
+
top: 0;
|
|
3230
|
+
right: 0;
|
|
3231
|
+
bottom: 0;
|
|
3232
|
+
left: 0;
|
|
3233
|
+
|
|
3234
|
+
display: block;
|
|
3235
|
+
|
|
3236
|
+
border-radius: inherit;
|
|
3237
|
+
|
|
3238
|
+
content: '';
|
|
3239
|
+
pointer-events: none;
|
|
3240
|
+
|
|
3241
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
3242
|
+
--my-card-border-color
|
|
3243
|
+
);
|
|
3244
|
+
transition: box-shadow 100ms;
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
```
|
|
3248
|
+
|
|
3249
|
+
Check out [the corresponding example](/cards/tiles/#newsletter-tiles).
|
|
3250
|
+
|
|
3251
|
+
#### 4\. Optional additions
|
|
3252
|
+
|
|
3253
|
+
##### 4.1 Glow hover effect
|
|
3254
|
+
|
|
3255
|
+
When applying hover effects like the `useGlowHover` effect, ensure the card's state changes are done correctly.
|
|
3256
|
+
|
|
3257
|
+
For example, instead of modifying the border for `:hover` and `:active` states, use the `enableBurst` option of the `useGlowHover` hook for smoother transitions:
|
|
3258
|
+
|
|
3259
|
+
```jsx
|
|
3260
|
+
import cn from 'classnames';
|
|
3261
|
+
import { textCn } from '@rescui/typography';
|
|
3262
|
+
import { useGlowHover } from '@rescui/use-glow-hover';
|
|
3263
|
+
|
|
3264
|
+
const Demo = () => {
|
|
3265
|
+
const ref = useGlowHover({ preset: 'transparent-blue', enableBurst: true });
|
|
3266
|
+
|
|
3267
|
+
return (
|
|
3268
|
+
<button
|
|
3269
|
+
type="button"
|
|
3270
|
+
ref={ref}
|
|
3271
|
+
className="my-card"
|
|
3272
|
+
style={{ maxWidth: 400 }}
|
|
3273
|
+
>
|
|
3274
|
+
<h3 className={textCn('rs-h3')}>Card title</h3>
|
|
3275
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-24')}>
|
|
3276
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab animi
|
|
3277
|
+
aspernatur autem commodi consequuntur esse laudantium nobis numquam
|
|
3278
|
+
officiis omnis, perferendis perspiciatis, possimus qui quidem, quis
|
|
3279
|
+
repellat tempore unde veniam?
|
|
3280
|
+
</p>
|
|
3281
|
+
</button>
|
|
3282
|
+
);
|
|
3283
|
+
};
|
|
3284
|
+
|
|
3285
|
+
render(<Demo />);
|
|
3286
|
+
```
|
|
3287
|
+
|
|
3288
|
+
Remove the need to adjust borders for hover states in the CSS:
|
|
3289
|
+
|
|
3290
|
+
```postcss
|
|
3291
|
+
.my-card {
|
|
3292
|
+
--my-card-border-width: 1px;
|
|
3293
|
+
--my-card-border-color: var(--rs-color-black-t20);
|
|
3294
|
+
|
|
3295
|
+
position: relative;
|
|
3296
|
+
|
|
3297
|
+
display: block;
|
|
3298
|
+
|
|
3299
|
+
overflow: hidden;
|
|
3300
|
+
|
|
3301
|
+
box-sizing: border-box;
|
|
3302
|
+
min-height: 0;
|
|
3303
|
+
|
|
3304
|
+
border: none;
|
|
3305
|
+
border-radius: 8px;
|
|
3306
|
+
|
|
3307
|
+
cursor: pointer;
|
|
3308
|
+
text-align: inherit;
|
|
3309
|
+
|
|
3310
|
+
background-color: var(--rs-color-white);
|
|
3311
|
+
box-shadow: inset 0 0 0 var(--my-card-border-width) var(
|
|
3312
|
+
--my-card-border-color
|
|
3313
|
+
);
|
|
3314
|
+
|
|
3315
|
+
appearance: none;
|
|
3316
|
+
padding: 24px;
|
|
3317
|
+
|
|
3318
|
+
&,
|
|
3319
|
+
&:hover {
|
|
3320
|
+
text-decoration: none;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
&:focus-visible {
|
|
3324
|
+
outline: none;
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
```
|
|
3328
|
+
|
|
3329
|
+
Check out [the corresponding example](/cards/clickable/#with-glow-hover).
|
|
3330
|
+
|
|
3331
|
+
##### 4.2 Forward states to the action element inside
|
|
3332
|
+
|
|
3333
|
+
It is recommended to forward states such as `:hover`, `:active`, and `:focus-visible` from the clickable card to the interactive elements inside, such as `button` or `rs-link`. This ensures that the inner elements behave consistently with the overall card's interaction states.
|
|
3334
|
+
|
|
3335
|
+
This can be achieved using [PostCSS mixins](https://github.com/postcss/postcss-mixins).
|
|
3336
|
+
|
|
3337
|
+
##### Button inside the card
|
|
3338
|
+
|
|
3339
|
+
Here is an example of using a `Button` component within the card:
|
|
3340
|
+
|
|
3341
|
+
```postcss
|
|
3342
|
+
@import '@rescui/button/lib/public-api.p.css';
|
|
3343
|
+
|
|
3344
|
+
.my-card {
|
|
3345
|
+
/* ... */
|
|
3346
|
+
|
|
3347
|
+
@media (hover: hover) {
|
|
3348
|
+
&:hover &__button {
|
|
3349
|
+
@mixin rs-button-state-hover;
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
&:active &__button {
|
|
3354
|
+
@mixin rs-button-state-active;
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
&:focus-visible &__button {
|
|
3358
|
+
@mixin rs-button-state-focus-visible;
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
&:active:focus-visible:not(:hover) &__button {
|
|
3362
|
+
@mixin rs-button-state-active-focus-visible-no-hover;
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
```
|
|
3366
|
+
|
|
3367
|
+
Additionally, remember to make the `Button` a static element inside the card:
|
|
3368
|
+
|
|
3369
|
+
```jsx
|
|
3370
|
+
import { Button } from '@rescui/button';
|
|
3371
|
+
|
|
3372
|
+
render(
|
|
3373
|
+
<Button notFocusable className="my-card__button">
|
|
3374
|
+
Download
|
|
3375
|
+
</Button>
|
|
3376
|
+
);
|
|
3377
|
+
```
|
|
3378
|
+
|
|
3379
|
+
Check out [the corresponding example](/cards/clickable/#with-action-button-inside).
|
|
3380
|
+
|
|
3381
|
+
##### rs-link inside the card
|
|
3382
|
+
|
|
3383
|
+
Similar behavior can also be applied when using an `rs-link` element within the card:
|
|
3384
|
+
|
|
3385
|
+
```postcss
|
|
3386
|
+
@import '@rescui/typography/lib/public-api.p.css';
|
|
3387
|
+
|
|
3388
|
+
.my-card {
|
|
3389
|
+
/* ... */
|
|
3390
|
+
|
|
3391
|
+
@media (hover: hover) {
|
|
3392
|
+
&:hover &__link {
|
|
3393
|
+
@mixin rs-typography-link-state-hover;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
&:focus-visible &__link {
|
|
3398
|
+
@mixin rs-typography-link-state-focus-visible;
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
```
|
|
3402
|
+
|
|
3403
|
+
As with the `Button`, ensure the `rs-link` is a static element:
|
|
3404
|
+
|
|
3405
|
+
```jsx
|
|
3406
|
+
import cn from 'classnames';
|
|
3407
|
+
import { textCn } from '@rescui/typography';
|
|
3408
|
+
|
|
3409
|
+
render(
|
|
3410
|
+
<span className={cn(textCn('rs-link'), 'my-card__link')}>Download</span>
|
|
3411
|
+
);
|
|
3412
|
+
```
|
|
3413
|
+
|
|
3414
|
+
Check out [the corresponding example](/cards/clickable/#with-action-link-inside).
|
|
3415
|
+
|
|
3416
|
+
### Tiles
|
|
3417
|
+
|
|
3418
|
+
Tiles are non-clickable and generally non-interactive cards.
|
|
3419
|
+
|
|
3420
|
+
They are simpler to create compared to clickable cards. However, their complexity may arise from additional elements such as interaction with `StickyTag`, content alignment with subgrids, or using flexbox.
|
|
3421
|
+
|
|
3422
|
+
#### Template: With Border
|
|
3423
|
+
|
|
3424
|
+
To maintain consistent presentation across all cards, it is recommended to use the `box-shadow` property as a border for tiles:
|
|
3425
|
+
|
|
3426
|
+
```postcss
|
|
3427
|
+
.my-tile {
|
|
3428
|
+
position: relative;
|
|
3429
|
+
|
|
3430
|
+
display: block;
|
|
3431
|
+
|
|
3432
|
+
box-sizing: border-box;
|
|
3433
|
+
min-height: 0;
|
|
3434
|
+
|
|
3435
|
+
border: none;
|
|
3436
|
+
border-radius: 8px;
|
|
3437
|
+
|
|
3438
|
+
background-color: var(--rs-color-white);
|
|
3439
|
+
box-shadow: inset 0 0 0 1px var(--rs-color-black-t20);
|
|
3440
|
+
|
|
3441
|
+
appearance: none;
|
|
3442
|
+
padding: 24px;
|
|
3443
|
+
}
|
|
3444
|
+
```
|
|
3445
|
+
|
|
3446
|
+
#### Template: Without Border
|
|
3447
|
+
|
|
3448
|
+
If you decide not to use a border, the tile should have a contrasting background to differentiate it from the surrounding content:
|
|
3449
|
+
|
|
3450
|
+
```postcss
|
|
3451
|
+
.my-tile {
|
|
3452
|
+
position: relative;
|
|
3453
|
+
|
|
3454
|
+
display: block;
|
|
3455
|
+
|
|
3456
|
+
box-sizing: border-box;
|
|
3457
|
+
min-height: 0;
|
|
3458
|
+
|
|
3459
|
+
border: none;
|
|
3460
|
+
border-radius: 8px;
|
|
3461
|
+
|
|
3462
|
+
background-color: var(--rs-color-grey-5);
|
|
3463
|
+
|
|
3464
|
+
padding: 24px;
|
|
3465
|
+
}
|
|
3466
|
+
```
|
|
3467
|
+
|
|
3468
|
+
#### Template: With a stretched image
|
|
3469
|
+
|
|
3470
|
+
This approach aims to stretch the image to cover the edges of the tile. Using CSS variables here makes it easier to manage the layout.
|
|
3471
|
+
|
|
3472
|
+
To display the border above stretched content, use a pseudo-element instead of a simple `box-shadow`. Also, do not use a transparent color for the border — use a solid one instead.
|
|
3473
|
+
|
|
3474
|
+
```tsx
|
|
3475
|
+
import cn from 'classnames';
|
|
3476
|
+
import { textCn } from '@rescui/typography';
|
|
3477
|
+
import tileImage from '@/parts/cards/newsletter1.webp';
|
|
3478
|
+
|
|
3479
|
+
const Demo = () => {
|
|
3480
|
+
return (
|
|
3481
|
+
<div className="my-tile-with-image" style={{ maxWidth: 450 }}>
|
|
3482
|
+
<img
|
|
3483
|
+
className="my-tile-with-image__img"
|
|
3484
|
+
alt="keymaps"
|
|
3485
|
+
src={tileImage.src}
|
|
3486
|
+
/>
|
|
3487
|
+
<h3 className={textCn('rs-h3')}>
|
|
3488
|
+
JetBrains Community Newsletter (Monthly)
|
|
3489
|
+
</h3>
|
|
3490
|
+
<p className={cn(textCn('rs-text-2'), 'rs-docs-offset-top-16')}>
|
|
3491
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab ad aperiam
|
|
3492
|
+
atque aut debitis, distinctio harum nam nobis nulla pariatur
|
|
3493
|
+
perspiciatis.
|
|
3494
|
+
</p>
|
|
3495
|
+
</div>
|
|
3496
|
+
);
|
|
3497
|
+
};
|
|
3498
|
+
|
|
3499
|
+
render(<Demo />);
|
|
3500
|
+
```
|
|
3501
|
+
|
|
3502
|
+
```postcss attached
|
|
3503
|
+
.my-tile-with-image {
|
|
3504
|
+
--my-tile-padding: 24px;
|
|
3505
|
+
|
|
3506
|
+
position: relative;
|
|
3507
|
+
|
|
3508
|
+
overflow: hidden;
|
|
3509
|
+
|
|
3510
|
+
box-sizing: border-box;
|
|
3511
|
+
min-height: 0;
|
|
3512
|
+
padding: var(--my-tile-padding);
|
|
3513
|
+
|
|
3514
|
+
border: none;
|
|
3515
|
+
border-radius: 8px;
|
|
3516
|
+
|
|
3517
|
+
background-color: var(--rs-color-white);
|
|
3518
|
+
|
|
3519
|
+
&__img {
|
|
3520
|
+
position: relative;
|
|
3521
|
+
top: calc(-1 * var(--my-tile-padding));
|
|
3522
|
+
left: calc(-1 * var(--my-tile-padding));
|
|
3523
|
+
|
|
3524
|
+
display: flex;
|
|
3525
|
+
justify-content: center;
|
|
3526
|
+
align-items: center;
|
|
3527
|
+
|
|
3528
|
+
width: calc(100% + 2 * var(--my-tile-padding));
|
|
3529
|
+
height: 212px;
|
|
3530
|
+
max-height: 212px;
|
|
3531
|
+
object-fit: cover;
|
|
3532
|
+
object-position: right;
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
&::after {
|
|
3536
|
+
position: absolute;
|
|
3537
|
+
top: 0;
|
|
3538
|
+
right: 0;
|
|
3539
|
+
bottom: 0;
|
|
3540
|
+
left: 0;
|
|
3541
|
+
|
|
3542
|
+
display: block;
|
|
3543
|
+
|
|
3544
|
+
border-radius: inherit;
|
|
3545
|
+
|
|
3546
|
+
content: '';
|
|
3547
|
+
pointer-events: none;
|
|
3548
|
+
|
|
3549
|
+
box-shadow: inset 0 0 0 1px var(--rs-color-grey-20);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
```
|
|
3553
|
+
|
|
3554
|
+
#### Template: Aligning Content Between Sibling Cards
|
|
3555
|
+
|
|
3556
|
+
In certain scenarios, content across sibling cards needs to be aligned so that it appears at the same height.
|
|
3557
|
+
|
|
3558
|
+
One way to achieve this is by using flexbox with `flex-direction: column`. However, this method works only for basic cases. When the content has dynamic or differing heights between cards, it may not be sufficient.
|
|
3559
|
+
|
|
3560
|
+
The preferred approach for more complex layouts is to use [CSS Grid’s subgrid feature](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid). Keep in mind this approach has certain [browser support limitations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid#browser_compatibility). Although subgrid layouts remain functional in unsupported browsers, you should carefully test their regression and viability in older browsers.
|
|
3561
|
+
|
|
3562
|
+
```tsx
|
|
3563
|
+
import { textCn } from '@rescui/typography';
|
|
3564
|
+
|
|
3565
|
+
const AlignedTile = ({ title, children }) => {
|
|
3566
|
+
return (
|
|
3567
|
+
<div className="my-aligned-tile">
|
|
3568
|
+
<div className="my-aligned-tile__content">
|
|
3569
|
+
<h3 className={textCn('rs-h3')}>{title}</h3>
|
|
3570
|
+
</div>
|
|
3571
|
+
<div className="my-aligned-tile__footer">
|
|
3572
|
+
<p className={textCn('rs-text-2')}>{children}</p>
|
|
3573
|
+
</div>
|
|
3574
|
+
</div>
|
|
3575
|
+
);
|
|
3576
|
+
};
|
|
3577
|
+
|
|
3578
|
+
const Demo = () => {
|
|
3579
|
+
return (
|
|
3580
|
+
<div className="my-aligned-tiles-container">
|
|
3581
|
+
<AlignedTile
|
|
3582
|
+
title={'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'}
|
|
3583
|
+
>
|
|
3584
|
+
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
|
|
3585
|
+
enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
|
3586
|
+
aliquip ex ea commodo consequat.
|
|
3587
|
+
</AlignedTile>
|
|
3588
|
+
<AlignedTile title={'Where are nebulae?'}>
|
|
3589
|
+
The closest known nebula to Earth is called the Helix Nebula. It is the
|
|
3590
|
+
remnant of a dying star — possibly one like the Sun. It is approximately
|
|
3591
|
+
700 light-years away from Earth.
|
|
3592
|
+
</AlignedTile>
|
|
3593
|
+
<AlignedTile title={'The drive to develop'}>
|
|
3594
|
+
We help developers work faster by automating common, repetitive tasks to
|
|
3595
|
+
enable them to stay focused on code design and the big picture. We
|
|
3596
|
+
provide tools to explore and familiarize with code bases faster.
|
|
3597
|
+
</AlignedTile>
|
|
3598
|
+
</div>
|
|
3599
|
+
);
|
|
3600
|
+
};
|
|
3601
|
+
|
|
3602
|
+
render(<Demo />);
|
|
3603
|
+
```
|
|
3604
|
+
|
|
3605
|
+
```postcss
|
|
3606
|
+
.my-aligned-tiles-container {
|
|
3607
|
+
display: grid;
|
|
3608
|
+
grid-template-rows: auto;
|
|
3609
|
+
grid-template-columns: repeat(3, 1fr);
|
|
3610
|
+
column-gap: 32px;
|
|
3611
|
+
row-gap: 24px;
|
|
3612
|
+
|
|
3613
|
+
@media (--md) {
|
|
3614
|
+
grid-template-columns: repeat(2, 1fr);
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
@media (--sm) {
|
|
3618
|
+
grid-template-columns: repeat(1, 1fr);
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
|
|
3622
|
+
.my-aligned-tile {
|
|
3623
|
+
position: relative;
|
|
3624
|
+
|
|
3625
|
+
display: grid;
|
|
3626
|
+
overflow: hidden;
|
|
3627
|
+
|
|
3628
|
+
box-sizing: border-box;
|
|
3629
|
+
min-height: 0;
|
|
3630
|
+
padding: 24px;
|
|
3631
|
+
|
|
3632
|
+
border: none;
|
|
3633
|
+
border-radius: 8px;
|
|
3634
|
+
|
|
3635
|
+
background-color: var(--rs-color-white);
|
|
3636
|
+
grid-template-columns: 1fr;
|
|
3637
|
+
grid-template-rows: subgrid;
|
|
3638
|
+
grid-row: auto / span 2;
|
|
3639
|
+
row-gap: 16px;
|
|
3640
|
+
box-shadow: inset 0 0 0 1px var(--rs-color-black-t20);
|
|
3641
|
+
|
|
3642
|
+
&__content {
|
|
3643
|
+
grid-row: 1;
|
|
3644
|
+
}
|
|
3645
|
+
|
|
3646
|
+
&__footer {
|
|
3647
|
+
grid-row: 2;
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
```
|