Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(web): Introduce responsive layouts of Card #DS-1559 #1796

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/web/src/scss/components/Card/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ Card can be displayed in a vertical, horizontal, or reversed horizontal layout.
👉 Keep in mind that, no matter the layout, the Card subcomponents must be arranged in the order
[specified above](#card-1).

### Responsive Card Layout

Card layout can be adjusted based on the breakpoint. To create a responsive layout, use the `tablet` and `desktop`
infixes, e.g. `Card--tablet--horizontal` or `Card--desktop--vertical`.

```html
<article class="Card Card--vertical Card--tablet--horizontal Card--desktop--horizontalReversed">
<!---->
</article>
```

### Boxed Cards

Card can be displayed with a border and a box shadow on hover.
Expand Down
205 changes: 100 additions & 105 deletions packages/web/src/scss/components/Card/_Card.scss
Original file line number Diff line number Diff line change
@@ -1,67 +1,42 @@
// 1. Get ready for card link overlay.
// 2. If there is a expanded CardMedia in a boxed vertical card, replace card padding with empty grid columns/rows.
// 3. Allow shrinking in grid layouts.
// 4. Make sure links and buttons are clickable.
// 5. Make text content selectable when there is a stretched CardLink.

// 2. Allow shrinking in grid layouts.
// 3. Make sure links and buttons are clickable.
// 4. Make text content selectable when there is a stretched CardLink.
// 5. Prepare card layouts for different breakpoints.
// a) Layouts are common for all breakpoints, so they are defined once in the theme file.
// b) Layout settings are applied to the grid via custom properties.
// c) If there is an expanded CardMedia in a boxed card, replace card padding with empty grid columns/rows.
// d) Padding is adjusted for different breakpoints.

@use 'sass:map';
@use '@tokens' as tokens;
@use '../../tools/breakpoint';
@use '../../tools/typography';
@use 'theme';

.Card {
--#{tokens.$css-variable-prefix}card-padding: #{theme.$padding};

@include typography.generate(theme.$typography);

position: relative; // 1.
display: grid;
min-width: 0; // 3.
grid-template-columns: var(--#{tokens.$css-variable-prefix}card-columns); // 5.b
grid-template-rows: var(--#{tokens.$css-variable-prefix}card-rows); // 5.b
grid-template-areas: var(--#{tokens.$css-variable-prefix}card-areas); // 5.b
column-gap: var(--#{tokens.$css-variable-prefix}card-column-gap); // 5.b
min-width: 0; // 2.
color: theme.$color;

@include breakpoint.up(tokens.$breakpoint-tablet) {
--#{tokens.$css-variable-prefix}card-padding: #{theme.$padding-tablet};
}
}

// 4., 5.
// 3., 4.
.Card:has(.CardLink) :where(a:not(.CardLink), button),
.Card:has(.CardLink):has(.CardBody--selectable) :where(p, ul, ol, dl) {
position: relative;
z-index: 1;
}

.Card--vertical {
grid-template-rows: auto 1fr auto;
grid-template-areas:
'media'
'body'
'footer';
}

.Card--horizontal {
grid-template-columns: auto 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
'media logo'
'media body'
'media footer';
}

.Card--horizontalReversed {
grid-template-columns: 1fr auto;
grid-template-rows: auto 1fr auto;
grid-template-areas:
'logo media'
'body media'
'footer media';
}

:is(.Card--horizontal, .Card--horizontalReversed):has(.CardArtwork:not(:only-child), .CardMedia:not(:only-child)) {
column-gap: theme.$gap;
}

.Card--boxed {
--#{tokens.$css-variable-prefix}card-padding: #{theme.$padding}; // 5.d

border: theme.$border-width theme.$border-style theme.$border-color;
border-radius: theme.$border-radius;
background-color: theme.$background-color;
Expand All @@ -76,77 +51,97 @@
}
}

&:not(:has(.CardMedia--expanded)) {
padding: var(--#{tokens.$css-variable-prefix}card-padding);
&:has(.CardMedia--expanded) {
padding: var(--#{tokens.$css-variable-prefix}card-padding-shorthand); // 5.b
}
}

.Card--boxed.Card--vertical {
&:has(.CardMedia--expanded) {
grid-template-columns:
var(--#{tokens.$css-variable-prefix}card-padding)
1fr
var(--#{tokens.$css-variable-prefix}card-padding); // 2.

grid-template-rows:
var(--#{tokens.$css-variable-prefix}card-padding)
auto
1fr
auto;
grid-template-areas:
'media media media'
'media media media'
'. body .'
'. footer .';
&:not(:has(.CardMedia--expanded)) {
padding: var(--#{tokens.$css-variable-prefix}card-padding);
}

&:has(.CardMedia:not(:only-child)) {
padding-bottom: var(--#{tokens.$css-variable-prefix}card-padding);
@include breakpoint.up(tokens.$breakpoint-tablet) {
--#{tokens.$css-variable-prefix}card-padding: #{theme.$padding-tablet}; // 5.d
}
}

.Card--boxed.Card--horizontal {
&:has(.CardMedia--expanded) {
grid-template-columns: auto 1fr;
grid-template-rows:
var(--#{tokens.$css-variable-prefix}card-padding)
auto
1fr
auto
var(--#{tokens.$css-variable-prefix}card-padding); // 2.

grid-template-areas:
'media .'
'media logo'
'media body'
'media footer'
'media .';
}
// 5.
@each $breakpoint-name, $breakpoint-value in theme.$breakpoints {
$infix: breakpoint.get-modifier(infix, $breakpoint-name, $breakpoint-value);

&:has(.CardMedia:not(:only-child)) {
padding-right: var(--#{tokens.$css-variable-prefix}card-padding);
}
}
@include breakpoint.up($breakpoint-value) {
.Card--#{$infix}vertical {
--#{tokens.$css-variable-prefix}card-columns: #{map.get(theme.$layouts, vertical, columns)};
--#{tokens.$css-variable-prefix}card-rows: #{map.get(theme.$layouts, vertical, rows)};
--#{tokens.$css-variable-prefix}card-areas: #{map.get(theme.$layouts, vertical, areas)};
}

.Card--boxed.Card--horizontalReversed {
&:has(.CardMedia--expanded) {
grid-template-columns: 1fr auto;
grid-template-rows:
var(--#{tokens.$css-variable-prefix}card-padding)
auto
1fr
auto
var(--#{tokens.$css-variable-prefix}card-padding); // 2.

grid-template-areas:
'. media'
'logo media'
'body media'
'footer media'
'. media';
}
.Card--#{$infix}horizontal {
--#{tokens.$css-variable-prefix}card-columns: #{map.get(theme.$layouts, horizontal, columns)};
--#{tokens.$css-variable-prefix}card-rows: #{map.get(theme.$layouts, horizontal, rows)};
--#{tokens.$css-variable-prefix}card-areas: #{map.get(theme.$layouts, horizontal, areas)};
}

.Card--#{$infix}horizontalReversed {
--#{tokens.$css-variable-prefix}card-columns: #{map.get(theme.$layouts, horizontal-reversed, columns)};
--#{tokens.$css-variable-prefix}card-rows: #{map.get(theme.$layouts, horizontal-reversed, rows)};
--#{tokens.$css-variable-prefix}card-areas: #{map.get(theme.$layouts, horizontal-reversed, areas)};
}

.Card--#{$infix}vertical:has(.CardArtwork:not(:only-child), .CardMedia:not(:only-child)) {
--#{tokens.$css-variable-prefix}card-column-gap: 0;
}

:is(.Card--#{$infix}horizontal, .Card--#{$infix}horizontalReversed):has(
.CardArtwork:not(:only-child),
.CardMedia:not(:only-child)
adamkudrna marked this conversation as resolved.
Show resolved Hide resolved
) {
--#{tokens.$css-variable-prefix}card-column-gap: #{theme.$gap};
}

// 5.c
.Card--boxed.Card--#{$infix}vertical {
&:has(.CardMedia--expanded) {
--#{tokens.$css-variable-prefix}card-columns: #{map.get(theme.$layouts-boxed, vertical, columns)};
--#{tokens.$css-variable-prefix}card-rows: #{map.get(theme.$layouts-boxed, vertical, rows)};
--#{tokens.$css-variable-prefix}card-areas: #{map.get(theme.$layouts-boxed, vertical, areas)};
}

&:has(.CardMedia:not(:only-child)) {
padding-left: var(--#{tokens.$css-variable-prefix}card-padding);
&:has(.CardMedia:not(:only-child)) {
--#{tokens.$css-variable-prefix}card-padding-shorthand: 0 0
crishpeen marked this conversation as resolved.
Show resolved Hide resolved
var(--#{tokens.$css-variable-prefix}card-padding);
}
}

// 5.c
.Card--boxed.Card--#{$infix}horizontal {
&:has(.CardMedia--expanded) {
--#{tokens.$css-variable-prefix}card-columns: #{map.get(theme.$layouts-boxed, horizontal, columns)};
--#{tokens.$css-variable-prefix}card-rows: #{map.get(theme.$layouts-boxed, horizontal, rows)};
--#{tokens.$css-variable-prefix}card-areas: #{map.get(theme.$layouts-boxed, horizontal, areas)};
}

&:has(.CardMedia:not(:only-child)) {
--#{tokens.$css-variable-prefix}card-padding-shorthand: 0
var(--#{tokens.$css-variable-prefix}card-padding) 0 0;
}
}

// 5.c
.Card--boxed.Card--#{$infix}horizontalReversed {
&:has(.CardMedia--expanded) {
--#{tokens.$css-variable-prefix}card-columns: #{map.get(
theme.$layouts-boxed,
horizontal-reversed,
columns
)};
--#{tokens.$css-variable-prefix}card-rows: #{map.get(theme.$layouts-boxed, horizontal-reversed, rows)};
--#{tokens.$css-variable-prefix}card-areas: #{map.get(theme.$layouts-boxed, horizontal-reversed, areas)};
}

&:has(.CardMedia:not(:only-child)) {
--#{tokens.$css-variable-prefix}card-padding-shorthand: 0 0 0
var(--#{tokens.$css-variable-prefix}card-padding);
}
}
}
}
20 changes: 18 additions & 2 deletions packages/web/src/scss/components/Card/_CardArtwork.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
@use '@tokens' as tokens;
@use '../../tools/breakpoint';
@use '../../tools/dictionaries';
@use 'theme';

.CardArtwork {
display: grid;
grid-area: media;

&:not(:last-child) {
margin-bottom: var(--#{tokens.$css-variable-prefix}card-artwork-margin-bottom);
}
}

@include dictionaries.generate-alignments(
Expand All @@ -12,6 +18,16 @@
$axis: 'x'
);

.Card--vertical > .CardArtwork:not(:last-child) {
margin-bottom: theme.$gap;
@each $breakpoint-name, $breakpoint-value in theme.$breakpoints {
@include breakpoint.up($breakpoint-value) {
$infix: breakpoint.get-modifier(infix, $breakpoint-name, $breakpoint-value);

.Card--#{$infix}vertical > .CardArtwork {
--#{tokens.$css-variable-prefix}card-artwork-margin-bottom: #{theme.$gap};
}

:is(.Card--#{$infix}horizontal, .Card--#{$infix}horizontalReversed) > .CardArtwork {
--#{tokens.$css-variable-prefix}card-artwork-margin-bottom: 0;
}
}
}
34 changes: 24 additions & 10 deletions packages/web/src/scss/components/Card/_CardLogo.scss
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
@use '@tokens' as tokens;
@use '../../tools/breakpoint';
@use 'theme';

.CardLogo {
display: inline-flex;
grid-area: var(--#{tokens.$css-variable-prefix}card-logo-grid-area);
place-self: var(--#{tokens.$css-variable-prefix}card-logo-place-self);
align-items: center;
justify-content: center;
margin-right: var(--#{tokens.$css-variable-prefix}card-logo-margin-right);
border: theme.$logo-border-width theme.$logo-border-style theme.$logo-border-color;
border-radius: theme.$logo-border-radius;
background-color: theme.$logo-background-color;
}

.Card--vertical > .CardLogo {
grid-area: media;
place-self: end;
margin-right: theme.$logo-offset-horizontal;
&:not(:last-child) {
margin-bottom: var(--#{tokens.$css-variable-prefix}card-logo-margin-bottom);
}
}

:is(.Card--horizontal, .Card--horizontalReversed) > .CardLogo {
grid-area: logo;
justify-self: start;
@each $breakpoint-name, $breakpoint-value in theme.$breakpoints {
@include breakpoint.up($breakpoint-value) {
$infix: breakpoint.get-modifier(infix, $breakpoint-name, $breakpoint-value);

&:not(:last-child) {
margin-bottom: theme.$gap;
.Card--#{$infix}vertical > .CardLogo {
--#{tokens.$css-variable-prefix}card-logo-grid-area: media;
--#{tokens.$css-variable-prefix}card-logo-place-self: end;
--#{tokens.$css-variable-prefix}card-logo-margin-right: #{theme.$logo-offset-horizontal};
--#{tokens.$css-variable-prefix}card-logo-margin-bottom: 0;
}

:is(.Card--#{$infix}horizontal, .Card--#{$infix}horizontalReversed) > .CardLogo {
--#{tokens.$css-variable-prefix}card-logo-grid-area: logo;
--#{tokens.$css-variable-prefix}card-logo-place-self: start;
--#{tokens.$css-variable-prefix}card-logo-margin-right: 0;
--#{tokens.$css-variable-prefix}card-logo-margin-bottom: #{theme.$gap};
}
}
}
Loading
Loading