feat(design-system): add reusable Card primitive
Standalone, OnPush, content-projection container. Padding none/sm/md/lg, bordered and interactive variants. Colors/radius/shadow/spacing consumed via existing tenant CSS vars with fallbacks.
This commit is contained in:
1
src/app/shared/ui/card/card.component.html
Normal file
1
src/app/shared/ui/card/card.component.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<ng-content />
|
||||||
47
src/app/shared/ui/card/card.component.scss
Normal file
47
src/app/shared/ui/card/card.component.scss
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
background: var(--bg-primary, #fff);
|
||||||
|
border-radius: var(--radius-lg, 10px);
|
||||||
|
box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.06));
|
||||||
|
transition: box-shadow var(--transition-fast, 120ms ease),
|
||||||
|
border-color var(--transition-fast, 120ms ease);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host.app-card--bordered {
|
||||||
|
border: 1px solid var(--border-color, #e4e4e7);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host.app-card--interactive {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: var(--shadow-md, 0 4px 12px rgba(0, 0, 0, 0.08));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid var(--primary-color, #2f6e5d);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:host.app-card--pad-none {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host.app-card--pad-sm {
|
||||||
|
padding: var(--space-sm, 0.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host.app-card--pad-md {
|
||||||
|
padding: var(--space-md, 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host.app-card--pad-lg {
|
||||||
|
padding: var(--space-lg, 1.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
:host {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/app/shared/ui/card/card.component.ts
Normal file
24
src/app/shared/ui/card/card.component.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
||||||
|
|
||||||
|
export type CardPadding = 'none' | 'sm' | 'md' | 'lg';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-card',
|
||||||
|
standalone: true,
|
||||||
|
templateUrl: './card.component.html',
|
||||||
|
styleUrl: './card.component.scss',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
host: {
|
||||||
|
'[class.app-card--bordered]': 'bordered()',
|
||||||
|
'[class.app-card--interactive]': 'interactive()',
|
||||||
|
'[class.app-card--pad-none]': "padding() === 'none'",
|
||||||
|
'[class.app-card--pad-sm]': "padding() === 'sm'",
|
||||||
|
'[class.app-card--pad-md]': "padding() === 'md'",
|
||||||
|
'[class.app-card--pad-lg]': "padding() === 'lg'"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class CardComponent {
|
||||||
|
readonly padding = input<CardPadding>('md');
|
||||||
|
readonly bordered = input(true);
|
||||||
|
readonly interactive = input(false);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user