Skip to content

Commit

Permalink
Feat(web): Introduce Card component #DS-1397
Browse files Browse the repository at this point in the history
  • Loading branch information
adamkudrna committed Nov 12, 2024
1 parent df768e8 commit 4961e78
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 3 deletions.
2 changes: 1 addition & 1 deletion packages/web-react/src/components/Heading/Heading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const defaultProps: Partial<SpiritHeadingProps<ElementType, void, void>> = {
const Heading = <T extends ElementType, S = void, E = void>(props: SpiritHeadingProps<T, S, E>): JSX.Element => {
const propsWithDefaults = { ...defaultProps, ...props };
const { elementType: ElementTag, children, ...restProps } = propsWithDefaults;
const { classProps, props: modifiedProps } = useHeadingStyleProps({ ...restProps, elementType: ElementTag });
const { classProps, props: modifiedProps } = useHeadingStyleProps({ ...restProps });
const { styleProps, props: otherProps } = useStyleProps(modifiedProps);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ export interface HeadingStyles<T extends ElementType> {
/** className props */
classProps: string | null;
/** props to be passed to the input element */
props: HeadingProps<T>;
props: Omit<HeadingProps<T>, 'elementType'>;
}

export function useHeadingStyleProps<T extends ElementType, S = void, E = void>(
props: SpiritHeadingProps<T, S, E>,
props: Omit<SpiritHeadingProps<T, S, E>, 'elementType'>,
): HeadingStyles<T> {
const { size, emphasis, ...restProps } = props;

Expand Down
63 changes: 63 additions & 0 deletions packages/web/src/scss/components/Card/_Card.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 1. Get ready for card link overlay.

@use '../../tools/typography';
@use 'theme';

.Card {
--card-gap: #{theme.$gap};

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

position: relative; // 1.
display: grid;
color: theme.$color;
}

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

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

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

.Card--boxed {
--card-padding: #{theme.$padding};

border: theme.$border-width theme.$border-style theme.$border-color;
border-radius: theme.$border-radius;
background-color: theme.$background-color;

// TODO discuss interactions with designer
// @media (hover: hover) {
// &:hover .CardLink {
// text-decoration: underline;
// }
// }

&:has(.CardLink) {
@media (hover: hover) {
transition: box-shadow theme.$transition-duration theme.$transition-timing;

&:hover {
box-shadow: theme.$box-shadow-hover;
}
}
}
}
29 changes: 29 additions & 0 deletions packages/web/src/scss/components/Card/_CardActions.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 1. Restore interactions for individual actions.

@use '../../tools/dictionaries';
@use 'theme';
@use 'tools';

.CardActions {
@include tools.card-content-element();
@include tools.add-spacing();

z-index: 1;
display: flex;
flex-direction: row-reverse;
flex-wrap: wrap;
grid-area: footer;
gap: theme.$actions-gap;
pointer-events: none;
}

// stylelint-disable-next-line selector-max-universal -- 1.
.CardActions > * {
pointer-events: auto;
}

@include dictionaries.generate-alignments(
$class-name: 'CardActions',
$dictionary-values: theme.$actions-alignment-dictionary,
$axis: 'x'
);
16 changes: 16 additions & 0 deletions packages/web/src/scss/components/Card/_CardBody.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 1. Make perex selectable.

@use 'tools';

.CardBody {
@include tools.card-content-element();
@include tools.add-spacing();

grid-area: body;
}

// 1.
.CardBody > p {
position: relative;
z-index: 1;
}
5 changes: 5 additions & 0 deletions packages/web/src/scss/components/Card/_CardLink.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@use '../../tools/links';

.CardLink:first-of-type {
@include links.stretch();
}
39 changes: 39 additions & 0 deletions packages/web/src/scss/components/Card/_CardMedia.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@use '../../tools/dictionaries';
@use 'theme';
@use 'tools';

.CardMedia {
@include tools.add-spacing($spacing: theme.$gap-dense);

grid-area: header;
}

.CardMedia:not(.CardMedia--cap) {
@include tools.card-content-element();
}

@include dictionaries.generate-sizes($class-name: 'CardMedia', $sizes: theme.$media-sizes);

.CardMedia__canvas {
aspect-ratio: var(--spirit-card-media-ratio);
overflow: hidden;
border-radius: theme.$border-radius;
}

.CardMedia__canvas > :is(img, picture > img, video) {
width: 100%;
height: 100%;
object-fit: cover;
}

// stylelint-disable selector-max-class -- TODO reason
// TODO rewrite to CSS variables?
.Card--vertical.Card--boxed > .CardMedia--cap > .CardMedia__canvas {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}

.Card--horizontal.Card--boxed > .CardMedia--cap > .CardMedia__canvas {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
20 changes: 20 additions & 0 deletions packages/web/src/scss/components/Card/_CardTitle.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@use '../../tools/typography';
@use 'theme';

// TODO Separate subcomponents?
.Card__eyebrow {
@include typography.generate(theme.$eyebrow-typography);

margin-bottom: theme.$eyebrow-margin-bottom;
color: theme.$eyebrow-color;
}

.Card__heading {
@include typography.generate(theme.$heading-typography);

color: theme.$heading-color;

&:not(:last-child) {
margin-bottom: theme.$heading-margin-bottom;
}
}
46 changes: 46 additions & 0 deletions packages/web/src/scss/components/Card/_theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@use '@tokens' as tokens;
@use '../../settings/dictionaries';
@use '../../settings/transitions';

$gap: tokens.$space-900;
$gap-dense: tokens.$space-700;

$padding: tokens.$space-900;

$typography: tokens.$body-medium-regular;

$border-width: tokens.$border-width-100;
$border-style: solid;
$border-color: tokens.$border-basic;
$border-radius: tokens.$radius-300;

$background-color: tokens.$background-primary;
$color: tokens.$text-secondary;
$box-shadow-hover: tokens.$shadow-100;

$transition-duration: transitions.$duration-100;
$transition-timing: transitions.$timing-eased-in-out;

$media-border-radius: tokens.$radius-300;
$media-sizes: (
small: (
ratio: 2,
),
medium: (
ratio: 1.5,
),
large: (
ratio: 1,
),
);

$eyebrow-typography: tokens.$body-small-semibold;
$eyebrow-color: tokens.$text-tertiary;
$eyebrow-margin-bottom: tokens.$space-300;

$heading-typography: tokens.$heading-small-semibold;
$heading-color: tokens.$text-primary;
$heading-margin-bottom: tokens.$space-500;

$actions-alignment-dictionary: dictionaries.$alignments-x;
$actions-gap: tokens.$space-700;
33 changes: 33 additions & 0 deletions packages/web/src/scss/components/Card/_tools.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@use 'theme';

@mixin card-content-element($direction: 'vertical') {
@if $direction == 'vertical' {
padding-inline: var(--card-padding);

&:first-child {
padding-top: var(--card-padding);
}

&:last-child {
padding-bottom: var(--card-padding);
}
}

// TODO other directions
}

@mixin add-spacing($direction: 'vertical', $spacing: theme.$gap) {
@if $direction == 'vertical' {
&:not(:last-child) {
margin-bottom: $spacing;
}
} @else if $direction == 'horizontal' {
&:not(:last-child) {
margin-inline-end: $spacing;
}
} @else {
&:not(:first-child) {
margin-inline-start: $spacing;
}
}
}
38 changes: 38 additions & 0 deletions packages/web/src/scss/components/Card/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{{#> web/layout/default title="Card" parentPageName="Components" }}

<section class="UNSTABLE_Section">
<h2 class="docs-Heading">Default</h2>
<div class="docs-Stack docs-Stack--start">

<article class="Card Card--vertical Card--boxed">
<div class="CardMedia CardMedia--medium CardMedia--cap">
<div class="CardMedia__canvas">
<!-- User content, no component classes -->
<img
src="https://images.unsplash.com/photo-1506260408121-e353d10b87c7?q=80&w=2728&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
alt=""
/>
<!-- End user content -->
</div>
</div>
<div class="CardBody">
<div class="Card__eyebrow">Eyebrow title</div>
<h4 class="Card__heading">
<a href="#" class="CardLink">
Card heading
</a>
</h4>
<!-- User content, no component classes -->
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean fermentum risus id tortor. Integer lacinia. Sed vel lectus.</p>
<!-- End user content -->
</div>
<footer class="CardActions CardActions--alignmentXLeft">
<a href="#" class="Button Button--medium Button--primary">Primary</a>
<a href="#" class="Button Button--medium Button--secondary">Secondary</a>
</footer>
</article>

</div>
</section>

{{/web/layout/default }}
6 changes: 6 additions & 0 deletions packages/web/src/scss/components/Card/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@forward 'Card';
@forward 'CardActions';
@forward 'CardBody';
@forward 'CardLink';
@forward 'CardMedia';
@forward 'CardTitle';
1 change: 1 addition & 0 deletions packages/web/src/scss/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@forward 'Alert';
@forward 'Breadcrumbs';
@forward 'Button';
@forward 'Card';
@forward 'Checkbox';
@forward 'Collapse';
@forward 'Container';
Expand Down

0 comments on commit 4961e78

Please sign in to comment.