aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules/card/card.module.scss
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-10-17 19:46:08 +0200
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:14:41 +0100
commitc153f93dc8691a71dc76aad3dd618298da9d238a (patch)
tree9c116c1472bab5585f98bceee19cfeca5041360d /src/components/molecules/card/card.module.scss
parent006b15b467a5cd835a6eab1b49023100bdc8f2e6 (diff)
refactor(components): rewrite Card component
* make the component more generic * merge `<Summary />` and `<Comment />` styles into card component to avoid repeating the same structure * remove most of the props to use composition However the CSS is a bit complex because of the two variants... Also, the component should be refactored when the CSS pseudo-class `:has` has enough support: the provider and the `cover` and `meta` props should be removed.
Diffstat (limited to 'src/components/molecules/card/card.module.scss')
-rw-r--r--src/components/molecules/card/card.module.scss296
1 files changed, 296 insertions, 0 deletions
diff --git a/src/components/molecules/card/card.module.scss b/src/components/molecules/card/card.module.scss
new file mode 100644
index 0000000..65f92f6
--- /dev/null
+++ b/src/components/molecules/card/card.module.scss
@@ -0,0 +1,296 @@
+@use "../../../styles/abstracts/functions" as fun;
+
+$breakpoint: 50ch;
+
+.cover {
+ width: var(--cover-width, 100%);
+ height: var(--cover-height, fun.convert-px(150));
+ max-width: none;
+ position: relative;
+
+ > * {
+ width: 100%;
+ height: 100%;
+ max-width: 100%;
+ position: absolute;
+ inset: 0;
+ object-position: center;
+ object-fit: cover;
+ }
+}
+
+.title {
+ background: none;
+ padding: 0;
+}
+
+.body {
+ max-width: 80ch;
+ color: var(--color-fg);
+ font-weight: 400;
+}
+
+.actions {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center;
+ justify-content: var(--alignment);
+ column-gap: var(--spacing-md);
+ row-gap: var(--spacing-sm);
+ padding-block: var(--spacing-sm);
+}
+
+.meta {
+ font-size: var(--font-size-sm);
+}
+
+:where(.footer) .meta {
+ flex-flow: row wrap;
+}
+
+.header,
+.footer {
+ display: contents;
+}
+
+.card {
+ --card-padding: clamp(var(--spacing-sm), 3vw, var(--spacing-md));
+
+ column-gap: var(--spacing-md);
+ row-gap: var(--spacing-sm);
+ width: 100%;
+ height: 100%;
+ background: var(--color-bg);
+
+ &--variant-1 {
+ --cover-height: clamp(
+ #{fun.convert-px(100)},
+ calc(#{fun.convert-px(200)} - 10cqw),
+ #{fun.convert-px(200)}
+ );
+ }
+
+ &--variant-2 {
+ --cover-height: #{fun.convert-px(90)};
+ --cover-width: var(--cover-height);
+ }
+}
+
+:where(.card--variant-2) .cover {
+ border-radius: fun.convert-px(3);
+ box-shadow:
+ 0 0 0 fun.convert-px(1) var(--color-shadow-light),
+ fun.convert-px(2) fun.convert-px(2) 0 fun.convert-px(1) var(--color-shadow);
+}
+
+.wrapper {
+ container: card / inline-size;
+
+ &--centered {
+ .header,
+ .body,
+ .footer {
+ text-align: center;
+ }
+
+ .meta {
+ margin-inline: auto;
+ }
+ }
+
+ &--is-link {
+ --scale-up: 1.05;
+
+ &:not(:disabled):focus:not(:hover) {
+ text-decoration: none;
+
+ .title {
+ text-decoration: underline solid var(--color-primary) 0.3ex;
+ }
+ }
+ }
+}
+
+/* stylelint-disable no-descending-specificity -- Stylelint complains about
+ * specificity but I think it is clearer and DRY this way. */
+
+:where(.wrapper--is-block) .card--variant-2 {
+ border: fun.convert-px(1) solid var(--color-border);
+ box-shadow:
+ fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow-light),
+ fun.convert-px(4) fun.convert-px(4) fun.convert-px(3) fun.convert-px(-2)
+ var(--color-shadow);
+}
+
+@container card (width <= #{$breakpoint}) {
+ .card {
+ display: flex;
+ flex-flow: column wrap;
+ row-gap: var(--card-padding);
+
+ &:where(:not(&--has-cover)) {
+ padding: var(--card-padding);
+ }
+
+ &:where(&--has-cover) {
+ padding-block-end: var(--card-padding);
+
+ :where(.header, .footer) > *:not(.cover),
+ .body {
+ padding-inline: var(--card-padding);
+ }
+ }
+ }
+
+ :where(.card--variant-1) {
+ .cover {
+ --cover-width: 100%;
+ }
+
+ :where(.footer) .meta {
+ margin-block-start: auto;
+ justify-content: space-between;
+ }
+ }
+
+ :where(.card--variant-2) {
+ :where(.header) {
+ .cover,
+ .title {
+ margin-inline: auto;
+ }
+
+ .cover {
+ margin-block-start: var(--card-padding);
+ }
+ }
+ }
+}
+
+@container card (width > #{$breakpoint}) {
+ .card {
+ display: grid;
+ grid-auto-rows: max-content;
+ padding: var(--card-padding);
+ }
+
+ .cover,
+ .title,
+ .meta,
+ .body,
+ .actions {
+ grid-column: 1;
+ }
+
+ .card--variant-1 {
+ grid-auto-columns: minmax(30ch, 80ch) 1fr minmax(min-content, 25cqw);
+ }
+
+ :where(.wrapper--is-block) .card--variant-1 {
+ border: fun.convert-px(1) solid var(--color-primary-dark);
+ border-radius: fun.convert-px(3);
+ box-shadow:
+ fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) 0
+ var(--color-shadow),
+ fun.convert-px(3) fun.convert-px(3) fun.convert-px(3) fun.convert-px(-1)
+ var(--color-shadow-light),
+ fun.convert-px(5) fun.convert-px(5) fun.convert-px(7) fun.convert-px(-1)
+ var(--color-shadow-light);
+ }
+
+ .card--variant-2 {
+ grid-auto-columns: minmax(min-content, 15cqw) 1fr minmax(30ch, 80ch);
+ }
+
+ .cover {
+ grid-row-start: 1;
+ }
+
+ :where(.wrapper--is-link:not(:disabled, :hover):focus) .title {
+ text-decoration: underline solid var(--color-primary) 0.3ex;
+ }
+
+ :where(.card--has-cover .header:only-child) .cover:only-child {
+ --cover-width: calc(100% + (2 * var(--card-padding)));
+ --cover-height: calc(100% + (2 * var(--card-padding)));
+
+ margin-block-start: calc(var(--card-padding) * -1);
+ margin-inline-start: calc(var(--card-padding) * -1);
+ }
+
+ :where(.card--variant-1) {
+ :where(.header:only-child) > *:only-child,
+ .body:only-child,
+ :where(.footer:only-child) > *:only-child {
+ grid-column: 1 / span 2;
+ }
+
+ :where(.header:not(:only-child)) .cover,
+ :where(.header:only-child) .cover:not(:only-child),
+ :where(.footer:not(:only-child)) .meta,
+ :where(.footer:only-child) .meta:not(:only-child) {
+ grid-column: 3;
+ }
+
+ :where(.footer) .meta {
+ grid-row-start: 1;
+ flex-flow: column wrap;
+ }
+
+ :where(.body:first-child + .footer) .meta,
+ :where(.footer:only-child) .meta {
+ grid-row-end: 1;
+ }
+
+ :where(.header) .title,
+ :where(.header) .cover + .meta {
+ grid-row: 1;
+ align-self: center;
+ }
+ }
+
+ :where(.card--variant-1.card--has-cover .footer) .meta {
+ grid-row: 2 / span 3;
+ }
+
+ :where(.card--variant-1.card--no-footer-meta .header:not(:only-child)) .cover,
+ :where(.card--variant-1.card--no-footer-meta .header) .cover:not(:only-child),
+ :where(.card--variant-1.card--no-cover .footer) .meta {
+ --cover-height: 100%;
+
+ grid-row-end: span 4;
+ }
+
+ :where(.card--variant-2) {
+ .header:only-child > *,
+ .body:only-child,
+ .footer:only-child > * {
+ grid-column: 1 / span 2;
+ }
+
+ .header:only-child > .meta {
+ justify-self: center;
+ }
+
+ :where(.header:not(:only-child)) .meta,
+ .body,
+ :where(.footer:not(:only-child)) .actions,
+ :where(.footer:not(:only-child)) .meta {
+ grid-column: 3;
+ }
+
+ :where(.header) {
+ .cover,
+ .title {
+ grid-row: 1 / span 2;
+ justify-self: center;
+ }
+ }
+ }
+
+ :where(.card--variant-2.card--has-cover .header) .title {
+ margin-top: calc(var(--cover-height) + var(--spacing-sm));
+ }
+}
+
+/* stylelint-enable no-descending-specificity */