Files
michaelschiemer/resources/css/components/skeleton-loader.css
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

457 lines
10 KiB
CSS

/**
* Skeleton Loader Component
*
* Modern skeleton loading placeholders for lazy-loaded components.
* Uses CSS animations for smooth, performant loading states.
*
* Features:
* - Multiple skeleton types (text, card, list, table, feed)
* - Smooth shimmer animation
* - Responsive design
* - Customizable via CSS custom properties
* - Accessibility-friendly
*/
@layer components {
/* Base Skeleton Styles */
.skeleton {
--skeleton-bg: oklch(95% 0.01 280);
--skeleton-shimmer: oklch(98% 0.01 280);
--skeleton-duration: 1.5s;
--skeleton-radius: 0.5rem;
background: linear-gradient(
90deg,
var(--skeleton-bg) 0%,
var(--skeleton-shimmer) 50%,
var(--skeleton-bg) 100%
);
background-size: 200% 100%;
animation: skeleton-shimmer var(--skeleton-duration) infinite ease-in-out;
border-radius: var(--skeleton-radius);
opacity: 0.7;
/* Accessibility */
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
user-select: none;
pointer-events: none;
}
}
/* Shimmer Animation */
@keyframes skeleton-shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
/* Skeleton Container */
.skeleton-container {
padding: 1.5rem;
background: var(--color-bg, oklch(100% 0 0));
border-radius: var(--skeleton-radius);
border: 1px solid oklch(90% 0.01 280);
min-height: 150px;
position: relative;
overflow: hidden;
/* Loading indicator */
&::after {
content: 'Loading...';
position: absolute;
bottom: 0.75rem;
left: 50%;
transform: translateX(-50%);
font-size: 0.875rem;
color: oklch(60% 0.05 280);
opacity: 0.5;
pointer-events: none;
}
/* Hide loading text when content loads */
&[data-loaded="true"]::after {
display: none;
}
}
/* Text Skeleton */
.skeleton-text {
height: 1rem;
margin-bottom: 0.75rem;
border-radius: 0.25rem;
&:last-child {
margin-bottom: 0;
}
/* Width variants */
&--full {
width: 100%;
}
&--80 {
width: 80%;
}
&--60 {
width: 60%;
}
&--40 {
width: 40%;
}
/* Size variants */
&--lg {
height: 1.5rem;
}
&--sm {
height: 0.75rem;
}
}
/* Card Skeleton */
.skeleton-card {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1.5rem;
background: var(--color-bg, oklch(100% 0 0));
border-radius: var(--skeleton-radius);
border: 1px solid oklch(90% 0.01 280);
&__header {
display: flex;
align-items: center;
gap: 1rem;
}
&__avatar {
width: 48px;
height: 48px;
border-radius: 50%;
flex-shrink: 0;
}
&__title {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
&__image {
width: 100%;
height: 200px;
border-radius: 0.5rem;
}
&__content {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
&__footer {
display: flex;
gap: 1rem;
padding-top: 0.5rem;
border-top: 1px solid oklch(90% 0.01 280);
}
&__action {
height: 2.5rem;
flex: 1;
border-radius: 0.5rem;
}
}
/* List Skeleton */
.skeleton-list {
display: flex;
flex-direction: column;
gap: 1rem;
&__item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: var(--color-bg, oklch(100% 0 0));
border-radius: var(--skeleton-radius);
border: 1px solid oklch(90% 0.01 280);
}
&__icon {
width: 40px;
height: 40px;
border-radius: 0.5rem;
flex-shrink: 0;
}
&__content {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
&__action {
width: 80px;
height: 2rem;
border-radius: 0.5rem;
}
}
/* Table Skeleton */
.skeleton-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border-radius: var(--skeleton-radius);
overflow: hidden;
border: 1px solid oklch(90% 0.01 280);
&__row {
display: flex;
gap: 1rem;
padding: 1rem;
&:not(:last-child) {
border-bottom: 1px solid oklch(90% 0.01 280);
}
/* Header row */
&--header {
background: oklch(97% 0.01 280);
font-weight: 600;
}
}
&__cell {
flex: 1;
height: 1.5rem;
border-radius: 0.25rem;
&--narrow {
flex: 0 0 100px;
}
&--wide {
flex: 2;
}
}
}
/* Feed Skeleton */
.skeleton-feed {
display: flex;
flex-direction: column;
gap: 1.5rem;
&__item {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1.5rem;
background: var(--color-bg, oklch(100% 0 0));
border-radius: var(--skeleton-radius);
border: 1px solid oklch(90% 0.01 280);
}
&__header {
display: flex;
align-items: center;
gap: 0.75rem;
}
&__avatar {
width: 40px;
height: 40px;
border-radius: 50%;
flex-shrink: 0;
}
&__meta {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.375rem;
}
&__content {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
&__actions {
display: flex;
gap: 1rem;
padding-top: 0.75rem;
border-top: 1px solid oklch(90% 0.01 280);
}
&__action {
width: 80px;
height: 2rem;
border-radius: 0.5rem;
}
}
/* Stats Skeleton */
.skeleton-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
&__card {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 1.5rem;
background: var(--color-bg, oklch(100% 0 0));
border-radius: var(--skeleton-radius);
border: 1px solid oklch(90% 0.01 280);
}
&__label {
height: 1rem;
width: 60%;
border-radius: 0.25rem;
}
&__value {
height: 2.5rem;
width: 80%;
border-radius: 0.5rem;
}
&__trend {
height: 0.875rem;
width: 40%;
border-radius: 0.25rem;
}
}
/* Chart Skeleton */
.skeleton-chart {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1.5rem;
background: var(--color-bg, oklch(100% 0 0));
border-radius: var(--skeleton-radius);
border: 1px solid oklch(90% 0.01 280);
&__title {
height: 1.5rem;
width: 40%;
border-radius: 0.25rem;
}
&__graph {
display: flex;
align-items: flex-end;
gap: 0.5rem;
height: 200px;
padding: 1rem 0;
border-bottom: 2px solid oklch(90% 0.01 280);
}
&__bar {
flex: 1;
border-radius: 0.25rem;
min-height: 40px;
&:nth-child(1) {
height: 60%;
}
&:nth-child(2) {
height: 80%;
}
&:nth-child(3) {
height: 50%;
}
&:nth-child(4) {
height: 90%;
}
&:nth-child(5) {
height: 70%;
}
}
&__legend {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
&__legend-item {
height: 1rem;
width: 80px;
border-radius: 0.25rem;
}
}
/* Responsive adjustments */
@media (max-width: 768px) {
.skeleton-container {
padding: 1rem;
}
.skeleton-card {
padding: 1rem;
&__image {
height: 150px;
}
}
.skeleton-stats {
grid-template-columns: 1fr;
}
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
.skeleton {
--skeleton-bg: oklch(20% 0.01 280);
--skeleton-shimmer: oklch(25% 0.01 280);
}
.skeleton-container,
.skeleton-card,
.skeleton-list__item,
.skeleton-table,
.skeleton-feed__item,
.skeleton-stats__card,
.skeleton-chart {
background: oklch(15% 0.01 280);
border-color: oklch(25% 0.01 280);
}
.skeleton-table__row--header {
background: oklch(18% 0.01 280);
}
}
/* Accessibility: Reduced motion */
@media (prefers-reduced-motion: reduce) {
.skeleton {
animation: none;
opacity: 0.5;
}
}
}