`
* would (display, background, border, radius all picked up from
* Bootstrap's own selectors).
* 2. Bootstrap's `.card > .card-body`, `.card > .card-img-top`, etc.
* child-combinator selectors continue to match across the shadow
* boundary, because the shadow root has no wrapping element — every
* shadow node and every projected child is a flat-tree child of the
* host.
*
* @slot - Card body content.
* @slot header - Rendered in `.card-header`.
* @slot footer - Rendered in `.card-footer`.
* @slot image - Image rendered above the body (`.card-img-top`).
* @slot image-bottom - Image rendered after the footer (`.card-img-bottom`).
* @slot img-overlay - Content rendered inside `.card-img-overlay` on top of the image.
*/
export class BsCard extends BootstrapElement {
@property({ type: String }) variant?: Variant;
@property({ type: String, attribute: 'text-variant' }) textVariant?: Variant;
@property({ type: String, attribute: 'heading' }) heading?: string;
@property({ type: String }) subtitle?: string;
@property({ type: Boolean, attribute: 'no-body' }) noBody = false;
@property({ type: Boolean }) horizontal = false;
protected override hostClasses(): string {
const parts = ['card'];
if (this.variant) {
parts.push(`border-${this.variant}`);
parts.push(`text-bg-${this.variant}`);
}
if (this.textVariant) parts.push(`text-${this.textVariant}`);
return parts.join(' ');
}
override render() {
const hasImg = !!this.querySelector('[slot="image"]');
const hasImgBottom = !!this.querySelector('[slot="image-bottom"]');
const hasHeader = !!this.querySelector('[slot="header"]');
const hasFooter = !!this.querySelector('[slot="footer"]');
const hasOverlay = !!this.querySelector('[slot="img-overlay"]');
const body = this.noBody
? html`
`
: html`
${this.heading ? html`
${this.heading}
` : nothing}
${this.subtitle
? html`${this.subtitle}
`
: nothing}
`;
if (this.horizontal) {
// Row-based horizontal layout. Image slot becomes left column, body + header/footer stack on the right.
return html`
${hasImg
? html`
`
: nothing}
${hasHeader
? html``
: nothing}
${body}
${hasFooter
? html``
: nothing}
`;
}
return html`
${hasImg ? html`
` : nothing}
${hasOverlay
? html`
`
: nothing}
${hasHeader ? html`` : nothing}
${body}
${hasFooter ? html`` : nothing}
${hasImgBottom ? html`
` : nothing}
`;
}
}
defineElement('bs-card', BsCard);
declare global {
interface HTMLElementTagNameMap {
'bs-card': BsCard;
}
}