@layer components { /* File upload — card-based dropzone with drag-and-drop, current file preview, and pending file preview. */ .app-file-upload { position: relative; border: 1px solid var(--app-muted-border-color); border-radius: var(--app-border-radius); background: var(--app-card-background-color); overflow: hidden; transition: border-color 0.15s ease; margin-bottom: var(--app-spacing); } .app-file-upload.is-dragover { border-color: var(--app-primary); background: color-mix(in srgb, var(--app-primary) 5%, var(--app-card-background-color)); } /* ── Current file (server-side, shown when data-current-src is set) ── */ .app-file-upload-current { display: flex; align-items: center; gap: calc(var(--app-spacing) * 0.75); padding: calc(var(--app-spacing) * 0.75); } .app-file-upload-current-image { width: 48px; height: 48px; border-radius: calc(var(--app-border-radius) * 0.5); object-fit: cover; flex-shrink: 0; border: 1px solid var(--app-muted-border-color); background: var(--app-background-color); } .app-file-upload-current-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; } .app-file-upload-current-label { font-size: var(--text-sm); font-weight: var(--font-medium); color: var(--app-contrast); } .app-file-upload-current-actions { display: flex; gap: calc(var(--app-spacing) * 0.25); } .app-file-upload-current-actions button { background: none; border: none; padding: calc(var(--app-spacing) * 0.15) calc(var(--app-spacing) * 0.35); cursor: pointer; font-size: var(--text-xs); border-radius: var(--app-border-radius); transition: color 0.15s ease, background 0.15s ease; margin: 0; width: auto; line-height: var(--leading-normal); } .app-file-upload-replace-button { color: var(--app-primary); } .app-file-upload-replace-button:hover { background: color-mix(in srgb, var(--app-primary) 10%, transparent); } .app-file-upload-delete-button { color: var(--app-del-color); } .app-file-upload-delete-button:hover { background: color-mix(in srgb, var(--app-del-color) 10%, transparent); } .app-file-upload.has-current .app-file-upload-dropzone { display: none; } .app-file-upload:not(.has-current) .app-file-upload-current { display: none; } /* ── Dropzone (visible when no current file and no pending file) ── */ .app-file-upload-dropzone { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: calc(var(--app-spacing) * 0.5); padding: calc(var(--app-spacing) * 1.5) var(--app-spacing); cursor: pointer; text-align: center; color: var(--app-muted-color); border: 2px dashed var(--app-muted-border-color); border-radius: var(--app-border-radius); margin: calc(var(--app-spacing) * 0.5); transition: border-color 0.15s ease, color 0.15s ease; } .app-file-upload.is-dragover .app-file-upload-dropzone { border-color: var(--app-primary); color: var(--app-primary); } .app-file-upload-dropzone:hover { border-color: var(--app-primary); color: var(--app-primary); } .app-file-upload-dropzone-icon { font-size: var(--text-2xl); line-height: var(--leading-none); } .app-file-upload-dropzone-label { font-size: var(--text-sm); font-weight: var(--font-medium); } .app-file-upload-dropzone-hint { font-size: var(--text-xs); opacity: 0.7; } .app-file-upload.has-file .app-file-upload-dropzone, .app-file-upload.has-file .app-file-upload-current { display: none; } .app-file-upload.is-rejected { animation: file-upload-shake 0.4s ease; } .app-file-upload.is-rejected .app-file-upload-dropzone { border-color: var(--app-del-color); color: var(--app-del-color); } @keyframes file-upload-shake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-4px); } 40% { transform: translateX(4px); } 60% { transform: translateX(-2px); } 80% { transform: translateX(2px); } } /* ── Pending file preview (visible when new file selected) ── */ .app-file-upload-preview { display: none; align-items: center; gap: calc(var(--app-spacing) * 0.75); padding: calc(var(--app-spacing) * 0.75); } .app-file-upload.has-file .app-file-upload-preview { display: flex; } .app-file-upload-thumbnail { width: 48px; height: 48px; border-radius: calc(var(--app-border-radius) * 0.5); object-fit: cover; flex-shrink: 0; border: 1px solid var(--app-muted-border-color); background: var(--app-background-color); } .app-file-upload-file-icon { width: 48px; height: 48px; border-radius: calc(var(--app-border-radius) * 0.5); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: var(--text-xl); color: var(--app-muted-color); background: var(--app-background-color); border: 1px solid var(--app-muted-border-color); } .app-file-upload-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; } .app-file-upload-filename { font-size: var(--text-sm); font-weight: var(--font-medium); color: var(--app-contrast); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .app-file-upload-filesize { font-size: var(--text-xs); color: var(--app-muted-color); } .app-file-upload-clear { flex-shrink: 0; background: none; border: none; padding: calc(var(--app-spacing) * 0.25); cursor: pointer; color: var(--app-muted-color); font-size: var(--text-base); line-height: var(--leading-none); border-radius: var(--app-border-radius); transition: color 0.15s ease; margin: 0; width: auto; } .app-file-upload-clear:hover { color: var(--app-del-color); } /* ── Hidden native input ── */ .app-file-upload input[type="file"] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } }