From 1dfbf679216502b77699ad7dd2ac4ac95735829b Mon Sep 17 00:00:00 2001 From: Petr Date: Sun, 12 Jul 2026 12:55:29 +0200 Subject: [PATCH] style(web): polish native date, time and file inputs --- lib/volumen/web/views/layout.erb | 593 +++++++++++++++++++++++++++++++ 1 file changed, 593 insertions(+) diff --git a/lib/volumen/web/views/layout.erb b/lib/volumen/web/views/layout.erb index f91615d..fbbd185 100644 --- a/lib/volumen/web/views/layout.erb +++ b/lib/volumen/web/views/layout.erb @@ -554,6 +554,271 @@ @media (prefers-reduced-motion: reduce) { * { transition: none !important; } } + + /* --- Native form control polish (date / time / file) --- */ + /* HTML5 date and time inputs have a native popup that is drawn by + the OS and cannot be themed. We polish the input itself so it + matches the rest of the design and replace the OS picker + indicator with a themed one. The JS below replaces the popup + with a fully custom one when JavaScript runs. */ + input[type="date"], + input[type="time"], + input[type="datetime-local"] { + font: inherit; + font-size: 0.85rem; + color: var(--fg); + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 0.55rem 0.75rem; + width: 100%; + font-feature-settings: normal; + transition: border-color 0.12s, box-shadow 0.12s; + } + input[type="date"]:focus, + input[type="time"]:focus, + input[type="datetime-local"]:focus { + outline: none; + border-color: var(--accent); + box-shadow: var(--ring); + } + input[type="date"]::-webkit-calendar-picker-indicator, + input[type="time"]::-webkit-calendar-picker-indicator, + input[type="datetime-local"]::-webkit-calendar-picker-indicator { + opacity: 0.5; + cursor: pointer; + padding: 0 0.2rem; + margin: 0; + filter: invert(0.45); + transition: opacity 0.12s; + } + input[type="date"]:hover::-webkit-calendar-picker-indicator, + input[type="time"]:hover::-webkit-calendar-picker-indicator, + input[type="datetime-local"]:hover::-webkit-calendar-picker-indicator { + opacity: 1; + } + @media (prefers-color-scheme: dark) { + input[type="date"]::-webkit-calendar-picker-indicator, + input[type="time"]::-webkit-calendar-picker-indicator, + input[type="datetime-local"]::-webkit-calendar-picker-indicator { + filter: invert(0.85); + } + } + /* Firefox draws a small inner "X" clear button — tone it down. */ + input[type="date"]::-moz-clear, + input[type="time"]::-moz-clear { opacity: 0.5; } + + input[type="file"] { + font: inherit; + font-size: 0.82rem; + color: var(--fg-muted); + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 0.5rem 0.75rem; + width: 100%; + cursor: pointer; + transition: border-color 0.12s, box-shadow 0.12s; + } + input[type="file"]:focus { + outline: none; + border-color: var(--accent); + box-shadow: var(--ring); + } + input[type="file"]::file-selector-button { + font: inherit; + font-size: 0.82rem; + font-weight: 550; + padding: 0.4rem 0.75rem; + margin-right: 0.7rem; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface-2); + color: var(--fg); + cursor: pointer; + font-family: inherit; + transition: background 0.12s, border-color 0.12s, color 0.12s; + } + input[type="file"]::file-selector-button:hover { + background: var(--surface); + border-color: var(--border-strong); + } + input[type="file"]::file-selector-button:active { transform: translateY(1px); } + /* Firefox variant of the file selector button pseudo-element. */ + input[type="file"]::-webkit-file-upload-button { + font: inherit; + font-size: 0.82rem; + font-weight: 550; + padding: 0.4rem 0.75rem; + margin-right: 0.7rem; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface-2); + color: var(--fg); + cursor: pointer; + font-family: inherit; + } + + /* --- Custom date picker (progressive enhancement) --- */ + .date-input { position: relative; } + .date-input__native { display: none; } + .date-input__btn { + display: flex; + align-items: center; + gap: 0.55rem; + width: 100%; + font: inherit; + font-size: 0.85rem; + text-align: left; + background: var(--surface); + color: var(--fg); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 0.55rem 0.75rem; + cursor: pointer; + font-family: inherit; + transition: border-color 0.12s, box-shadow 0.12s, background 0.12s; + } + .date-input__btn:hover { border-color: var(--border-strong); } + .date-input__btn:focus-visible { + outline: none; + border-color: var(--accent); + box-shadow: var(--ring); + } + .date-input__btn[data-empty="1"] { color: var(--fg-subtle); } + .date-input__label { flex: 1; min-width: 0; } + .date-input__icon { + display: inline-flex; + color: var(--fg-muted); + flex-shrink: 0; + } + .date-input__icon svg { width: 16px; height: 16px; display: block; } + + .date-input__popover { + position: absolute; + z-index: 20; + top: calc(100% + 6px); + left: 0; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 0.85rem; + box-shadow: var(--shadow-lg); + min-width: 280px; + user-select: none; + } + .date-input__nav { + display: flex; + align-items: center; + gap: 0.4rem; + margin-bottom: 0.5rem; + } + .date-input__nav-title { + flex: 1; + text-align: center; + font-size: 0.9rem; + font-weight: 600; + color: var(--fg); + letter-spacing: -0.01em; + } + .date-input__nav-btn { + width: 28px; + height: 28px; + display: grid; + place-items: center; + background: transparent; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + color: var(--fg-muted); + cursor: pointer; + font-family: inherit; + transition: background 0.12s, color 0.12s, border-color 0.12s; + } + .date-input__nav-btn:hover { + background: var(--surface-2); + color: var(--fg); + border-color: var(--border-strong); + } + .date-input__nav-btn svg { width: 14px; height: 14px; } + .date-input__weekdays, + .date-input__days { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 2px; + } + .date-input__weekday { + font-size: 0.68rem; + font-weight: 600; + text-transform: uppercase; + color: var(--fg-subtle); + text-align: center; + padding: 0.35rem 0 0.4rem; + letter-spacing: 0.04em; + } + .date-input__day { + height: 32px; + display: grid; + place-items: center; + font: inherit; + font-size: 0.82rem; + background: transparent; + border: none; + border-radius: var(--radius-sm); + color: var(--fg); + cursor: pointer; + font-family: inherit; + transition: background 0.1s, color 0.1s; + } + .date-input__day:hover { background: var(--surface-2); } + .date-input__day[data-other-month="1"] { color: var(--fg-subtle); } + .date-input__day[data-today="1"] { + border: 1px solid var(--border-strong); + } + .date-input__day[data-selected="1"] { + background: var(--accent); + color: var(--on-accent); + } + .date-input__day[data-selected="1"]:hover { background: var(--accent-hover); } + .date-input__day:focus-visible { + outline: none; + box-shadow: var(--ring); + } + .date-input__footer { + display: flex; + gap: 0.4rem; + margin-top: 0.85rem; + padding-top: 0.65rem; + border-top: 1px solid var(--border); + } + .date-input__footer-btn { + font: inherit; + font-size: 0.8rem; + font-weight: 550; + flex: 1; + padding: 0.4rem 0.6rem; + border-radius: var(--radius-sm); + border: 1px solid var(--border); + background: var(--surface); + color: var(--fg-muted); + cursor: pointer; + font-family: inherit; + transition: background 0.12s, color 0.12s, border-color 0.12s; + } + .date-input__footer-btn:hover { + background: var(--surface-2); + color: var(--fg); + border-color: var(--border-strong); + } + .date-input__footer-btn--primary { + background: var(--accent); + color: var(--on-accent); + border-color: var(--accent); + } + .date-input__footer-btn--primary:hover { + background: var(--accent-hover); + border-color: var(--accent-hover); + color: var(--on-accent); + } @@ -660,5 +925,333 @@ <% end %> + +