80 lines
1.8 KiB
JavaScript
80 lines
1.8 KiB
JavaScript
/**
|
|
* Skeleton Card Component
|
|
* Shows a loading placeholder for listing cards
|
|
*/
|
|
|
|
class SkeletonCard extends HTMLElement {
|
|
connectedCallback() {
|
|
this.render()
|
|
}
|
|
|
|
render() {
|
|
this.innerHTML = /* html */`
|
|
<div class="skeleton-image"></div>
|
|
<div class="skeleton-info">
|
|
<div class="skeleton-title"></div>
|
|
<div class="skeleton-price"></div>
|
|
<div class="skeleton-location"></div>
|
|
</div>
|
|
`
|
|
}
|
|
}
|
|
|
|
customElements.define('skeleton-card', SkeletonCard)
|
|
|
|
const style = document.createElement('style')
|
|
style.textContent = /* css */`
|
|
skeleton-card {
|
|
display: block;
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-md);
|
|
overflow: hidden;
|
|
}
|
|
|
|
skeleton-card .skeleton-image {
|
|
aspect-ratio: 1;
|
|
background: var(--color-bg-tertiary);
|
|
animation: skeleton-pulse 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
skeleton-card .skeleton-info {
|
|
padding: var(--space-sm);
|
|
}
|
|
|
|
skeleton-card .skeleton-title,
|
|
skeleton-card .skeleton-price,
|
|
skeleton-card .skeleton-location {
|
|
background: var(--color-bg-tertiary);
|
|
border-radius: var(--radius-sm);
|
|
animation: skeleton-pulse 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
skeleton-card .skeleton-title {
|
|
height: 1rem;
|
|
width: 80%;
|
|
margin-bottom: var(--space-xs);
|
|
}
|
|
|
|
skeleton-card .skeleton-price {
|
|
height: 1rem;
|
|
width: 50%;
|
|
margin-bottom: var(--space-xs);
|
|
}
|
|
|
|
skeleton-card .skeleton-location {
|
|
height: 0.75rem;
|
|
width: 60%;
|
|
}
|
|
|
|
@keyframes skeleton-pulse {
|
|
0%, 100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
`
|
|
document.head.appendChild(style)
|