openvibers/openvibe.shared ov-upload.css
ov-upload.css
| 1 | /* openvibe-shared ov-upload (designed 11-09): a picker that says what to do, each file a row with its own progress, |
| 2 | the total above them for two or more. The network's theme variables; dark and light; Calm keeps the fill, not the sheen. */ |
| 3 | .ov-upload { display: grid; gap: 12px; color: var(--text-primary, #e8ecf3); } |
| 4 | .ov-upload__input { position: absolute ; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } |
| 5 | .ov-upload__picker { display: grid; place-items: center; align-content: center; gap: 6px; width: 100%; min-height: 148px; padding: 22px; border: 1.5px dashed color-mix(in srgb, var(--accent, #3b82f6) 45%, var(--border, rgba(255,255,255,.14))); |
| 6 | border-radius: 18px; background: color-mix(in srgb, var(--accent, #3b82f6) 5%, transparent); color: inherit; font: inherit; cursor: pointer; transition: background .18s, border-color .18s, transform .18s; } |
| 7 | .ov-upload__picker strong { font-size: 15.5px; } |
| 8 | .ov-upload__picker:hover, .ov-upload.is-dragging .ov-upload__picker { border-color: var(--accent, #3b82f6); background: color-mix(in srgb, var(--accent, #3b82f6) 11%, transparent); } |
| 9 | .ov-upload.is-dragging .ov-upload__picker { transform: scale(1.01); border-style: solid; } |
| 10 | .ov-upload__picker:active { transform: scale(.99); } |
| 11 | .ov-upload__picker:focus-visible, .ov-upload__remove:focus-visible, .ov-progress__retry:focus-visible { outline: 2px solid var(--accent, #3b82f6); outline-offset: 3px; } |
| 12 | .ov-upload__icon { display: grid; place-items: center; width: 52px; height: 52px; margin-bottom: 4px; border-radius: 16px; color: var(--on-accent-strong, #fff); |
| 13 | background: linear-gradient(150deg, var(--accent, #3b82f6), var(--accent-strong, #2563eb)); box-shadow: 0 8px 22px color-mix(in srgb, var(--accent, #3b82f6) 35%, transparent); } |
| 14 | .ov-upload__icon svg, .ov-upload__preview svg, .ov-upload__remove svg { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } |
| 15 | .ov-upload__icon svg { width: 26px; height: 26px; } |
| 16 | .ov-upload.is-dragging .ov-upload__icon { animation: ov-upload-bob .9s ease-in-out infinite; } |
| 17 | .ov-upload__drop { font-size: 13px; color: var(--text-muted, #93a0b4); } |
| 18 | .ov-upload__total { padding: 0 2px; } |
| 19 | .ov-upload__total[hidden] { display: none; } |
| 20 | .ov-upload__rows { display: grid; gap: 8px; } |
| 21 | .ov-upload__row { display: grid; grid-template-columns: 52px minmax(0, 1fr) 40px; align-items: center; gap: 4px 12px; min-width: 0; padding: 10px; |
| 22 | border-radius: 14px; background: color-mix(in srgb, var(--text-primary, #e8ecf3) 4%, transparent); box-shadow: inset 0 0 0 1px var(--border, rgba(255,255,255,.08)); |
| 23 | animation: ov-upload-in .26s cubic-bezier(.2, .8, .2, 1) both; } |
| 24 | .ov-upload__preview { display: grid; place-items: center; width: 52px; height: 52px; overflow: hidden; border-radius: 10px; color: var(--accent-light, var(--accent, #60a5fa)); |
| 25 | background: color-mix(in srgb, var(--accent, #3b82f6) 12%, transparent); } |
| 26 | .ov-upload__preview svg { width: 24px; height: 24px; } |
| 27 | .ov-upload__preview img, .ov-upload__preview video { width: 100%; height: 100%; object-fit: cover; } |
| 28 | .ov-upload__file { display: grid; gap: 1px; min-width: 0; } |
| 29 | .ov-upload__file strong { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 14.5px; } |
| 30 | .ov-upload__file small { font-size: 12.5px; color: var(--text-muted, #93a0b4); } |
| 31 | .ov-upload__remove { display: grid; place-items: center; width: 40px; height: 40px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--text-secondary, #a8b3c4); cursor: pointer; } |
| 32 | .ov-upload__remove svg { width: 18px; height: 18px; } |
| 33 | @media (hover: hover) { .ov-upload__remove:hover { background: color-mix(in srgb, var(--text-primary, #e8ecf3) 9%, transparent); color: var(--text-primary, #e8ecf3); } } |
| 34 | .ov-upload__meter { grid-column: 1 / -1; } |
| 35 | /* Progress */ |
| 36 | .ov-progress { display: grid; gap: 7px; } |
| 37 | .ov-progress__heading { display: flex; justify-content: space-between; align-items: center; gap: 12px; font-size: 14px; font-weight: 650; } |
| 38 | .ov-progress__percent { font-variant-numeric: tabular-nums; } |
| 39 | .ov-progress__track { height: 8px; overflow: hidden; border-radius: 99px; background: color-mix(in srgb, var(--text-primary, #e8ecf3) 10%, transparent); } |
| 40 | .ov-progress__fill { position: relative; width: 0; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--accent-strong, #2563eb), var(--accent, #3b82f6)); transition: width .65s cubic-bezier(.2, .7, .2, 1); } |
| 41 | .ov-progress[data-state="sending"] .ov-progress__fill::after { content: ''; position: absolute; inset: 0; background: linear-gradient(110deg, transparent 25%, rgba(255,255,255,.3) 45%, transparent 65%); animation: ov-upload-sheen 1.8s linear infinite; } |
| 42 | .ov-progress[data-state="processing"] .ov-progress__fill { width: 100% ; animation: ov-upload-pulse 1.8s ease-in-out infinite; } |
| 43 | .ov-progress[data-state="done"] .ov-progress__fill { background: var(--success, #22c55e); } |
| 44 | .ov-progress[data-state="done"] .ov-progress__percent { color: var(--success, #22c55e); animation: ov-upload-check .38s ease-out both; } |
| 45 | .ov-progress__check { display: block; width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; } |
| 46 | .ov-progress__check path { stroke-dasharray: 24; stroke-dashoffset: 24; animation: ov-upload-draw .45s ease-out forwards; } |
| 47 | .ov-progress[data-state="failed"] .ov-progress__fill { background: var(--danger, #ef4444); } |
| 48 | .ov-progress[data-state="failed"] .ov-progress__label { color: var(--danger, #ef4444); } |
| 49 | .ov-progress__detail { min-height: 1.4em; margin: 0; font-size: 12.5px; color: var(--text-muted, #93a0b4); font-variant-numeric: tabular-nums; } |
| 50 | .ov-progress__retry { justify-self: start; min-height: 36px; padding: 0 14px; border: 0; border-radius: 999px; background: color-mix(in srgb, var(--text-primary, #e8ecf3) 9%, transparent); color: inherit; font: inherit; font-size: 13.5px; font-weight: 650; cursor: pointer; } |
| 51 | /* Inside a file's row: one line of state and percent, a thin bar, the numbers. */ |
| 52 | .ov-progress--row { gap: 5px; margin-top: 6px; } |
| 53 | .ov-progress--row .ov-progress__heading { font-size: 12.5px; color: var(--text-secondary, #a8b3c4); } |
| 54 | .ov-progress--row .ov-progress__track { height: 5px; } |
| 55 | .ov-progress--row .ov-progress__detail { font-size: 12px; } |
| 56 | @keyframes ov-upload-sheen { from { transform: translateX(-100%); } to { transform: translateX(100%); } } |
| 57 | @keyframes ov-upload-pulse { 50% { opacity: .5; } } |
| 58 | @keyframes ov-upload-check { from { transform: scale(.5); opacity: 0; } to { transform: scale(1); opacity: 1; } } |
| 59 | /* "You already uploaded this": the note, then the three ways on, as one block under the file's row. */ |
| 60 | .ov-upload__duplicate-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; padding: 10px 12px; border-radius: 14px; background: color-mix(in srgb, var(--accent, #3b82f6) 9%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent, #3b82f6) 24%, transparent); animation: ov-upload-in .3s cubic-bezier(.2, .9, .3, 1.1) both; } |
| 61 | .ov-upload__duplicate-note { flex: 1 0 100%; margin: 0 0 2px; font-size: 13.5px; font-weight: 650; color: var(--text-primary, #e6edf7); } |
| 62 | .ov-upload__duplicate-actions .ovk-btn { min-height: 40px; } |
| 63 | @media (pointer: coarse) { .ov-upload__duplicate-actions .ovk-btn { min-height: 44px; flex: 1 1 auto; } } |
| 64 | @keyframes ov-upload-draw { to { stroke-dashoffset: 0; } } |
| 65 | @keyframes ov-upload-in { from { opacity: 0; transform: translateY(6px); } } |
| 66 | @keyframes ov-upload-bob { 50% { transform: translateY(-3px); } } |
| 67 | @media (pointer: coarse), (max-width: 700px) { .ov-upload__picker { min-height: 120px; } .ov-upload__drop { display: none; } } |
| 68 | html[data-ov-motion=off] .ov-progress__check path{stroke-dashoffset: 0}html[data-ov-motion=off] .ov-progress[data-state="sending"] .ov-progress__fill::after{display: none} |
| 69 |