diff --git a/src/ui/app.css b/src/ui/app.css index 101d2967..f379afcd 100644 --- a/src/ui/app.css +++ b/src/ui/app.css @@ -519,50 +519,65 @@ fieldset:disabled :is(.color-options, .scanner-colours) label:not(:has(input:che .mobile-pairing li + li { margin-top: 8px; } .mobile-pairing .teaching-disclosure { margin-bottom: 0; } -/* --- Dialogs ------------------------------------------------------------------ */ -.modal-backdrop { position: fixed; z-index: 20; inset: 0; display: grid; place-items: center; padding: 12px; background: rgba(28, 27, 31, 0.48); } -.pairing-dialog, .profile-dialog, .switch-practice-dialog { width: min(100%, 900px); max-height: calc(100dvh - 24px); overflow: auto; border: 1px solid var(--border); border-radius: 8px; padding: 20px; color: var(--text); background: var(--surface); box-shadow: var(--shadow-dialog); } -.switch-practice-dialog { border-radius: 12px; } -.confirm-dialog { width: min(100%, 480px); } -.dialog-error { margin-top: 16px; border-left: 3px solid var(--status-error); padding: 9px 11px; color: var(--status-error); background: var(--status-error-soft); } - -.setup-dialog { display: flex; flex-direction: column; width: min(100%, 920px); height: min(900px, calc(100dvh - 24px)); min-height: 0; max-height: calc(100dvh - 24px); overflow: hidden; border: 1px solid var(--border); border-radius: 10px; padding: 20px; color: var(--text); background: var(--surface); box-shadow: var(--shadow-dialog); } -.setup-dialog:focus { outline: 2px solid var(--focus); outline-offset: 2px; } -.setup-dialog > header, .setup-dialog > footer { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; } -.setup-dialog > header { justify-content: space-between; } -.setup-dialog > header span { color: var(--brand); font-size: 16px; font-weight: 700; text-transform: uppercase; } -.setup-dialog > header h2 { margin-top: 4px; font-size: 24px; line-height: 1.33; } -.setup-dialog > header strong { color: var(--muted); font-size: 16px; } -.setup-progress { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin-top: 18px; } -.setup-progress span { height: 4px; border-radius: 2px; background: var(--border); } +/* --- Dialogs ------------------------------------------------------------------ + Every main-window dialog shares one shell: the same surface, radius, + padding and shadow; a header with the title and any close control; a body; + and a footer whose spacer pushes the main actions to the end. Opening is + the one moment of motion, and it is skipped under reduced motion. */ +.modal-backdrop { position: fixed; z-index: 20; inset: 0; display: grid; place-items: center; padding: 12px; background: rgba(28, 27, 31, 0.5); } +.setup-dialog, .pairing-dialog, .profile-dialog, .switch-practice-dialog, .capture-dialog { max-height: calc(100dvh - 24px); overflow: auto; border: 1px solid var(--border); border-radius: var(--radius-dialog); padding: 28px; color: var(--text); background: var(--surface); box-shadow: var(--shadow-dialog); } +.setup-dialog, .pairing-dialog, .profile-dialog, .switch-practice-dialog { animation: dialog-in 160ms ease-out; } +/* No fill mode, and never a lasting transform: the switch capture dialog + renders inside the setup guide with position: fixed, and a transform on the + guide would trap it inside the guide's clipped box instead of the window. */ +@keyframes dialog-in { from { opacity: 0; transform: translateY(8px) scale(0.985); } } +:is(.setup-dialog, .pairing-dialog, .profile-dialog, .switch-practice-dialog):focus { outline: none; } +:is(.setup-dialog, .pairing-dialog, .profile-dialog, .switch-practice-dialog):focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; } +.pairing-dialog { width: min(100%, 640px); } +.profile-dialog, .switch-practice-dialog { width: min(100%, 760px); } +.confirm-dialog { width: min(100%, 520px); } +:is(.setup-dialog, .pairing-dialog, .profile-dialog) > header h2, .switch-practice-dialog > h2 { font-size: 24px; line-height: 1.3; font-weight: 700; } +:is(.setup-dialog, .pairing-dialog, .profile-dialog) > header p { margin-top: 4px; color: var(--muted); } +:is(.setup-dialog, .profile-dialog) > footer { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 24px; border-top: 1px solid var(--border); padding-top: 20px; } +:is(.setup-dialog, .profile-dialog) > footer > span { flex: 1; } +.confirm-dialog > footer { border-top: 0; padding-top: 0; } +.dialog-error { margin-top: 16px; border-left: 4px solid var(--status-error); border-radius: 0 8px 8px 0; padding: 10px 14px; color: var(--status-error); background: var(--status-error-soft); } + +/* The setup guide sizes to its step, up to the window, and scrolls only its + middle so the step rail and the navigation stay in view. */ +.setup-dialog { display: flex; flex-direction: column; width: min(100%, 880px); min-height: min(480px, calc(100dvh - 24px)); overflow: hidden; } +.setup-dialog > header { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 8px 16px; } +.setup-dialog > header span { color: var(--muted); font-size: 16px; font-weight: 600; } +.setup-dialog > header h2 { margin-top: 2px; } +.setup-dialog > header strong { color: var(--muted); font-size: 16px; font-variant-numeric: tabular-nums; } +.setup-progress { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin-top: 16px; } +.setup-progress span { height: 6px; border-radius: 3px; background: var(--border); } .setup-progress span[data-active="true"] { background: var(--brand); } -.setup-content { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px 4px; } +.setup-content { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 24px 4px 8px; } +.setup-content > div > p { max-width: 68ch; } .setup-content p { margin-top: 8px; color: var(--muted); } -.setup-choices { margin-top: 22px; } -.setup-privacy { margin-top: 18px; color: var(--brand-label); } +.setup-choices { margin-top: 24px; } +.setup-privacy { margin-top: 16px; color: var(--brand-label); } .setup-navigation-guidance { flex: 0 0 auto; margin: 0; color: var(--muted); line-height: 1.5; } -.setup-navigation-guidance:not(:empty) { padding-bottom: 16px; } -.setup-dialog > footer { gap: 12px; border-top: 1px solid var(--border); padding-top: 12px; } -.setup-dialog > footer > span { flex: 1; } +.setup-navigation-guidance:not(:empty) { padding-top: 12px; } +.setup-dialog > footer { flex: none; margin-top: 12px; } .setup-backdrop[hidden] { display: none; } -.pairing-dialog:focus { outline: 2px solid var(--focus); outline-offset: 2px; } -.pairing-dialog > header { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; gap: 12px; } +.pairing-dialog > header { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; gap: 16px; } .pairing-dialog > header > svg { margin-top: 2px; color: var(--brand); } -.pairing-dialog > header > span { display: grid; place-items: center; min-width: 28px; height: 28px; border-radius: 14px; color: var(--on-brand-container); background: var(--brand-container); font-weight: 700; } +.pairing-dialog > header > span { display: grid; place-items: center; min-width: 32px; height: 32px; border-radius: 16px; color: var(--on-brand-container); background: var(--brand-container); font-weight: 700; } .pairing-dialog p { margin-top: 4px; color: var(--muted); line-height: 1.4; } .pairing-dialog .pairing-connection { color: var(--text); } -.pairing-list { margin-top: 20px; border-top: 1px solid var(--border); } -.pairing-list article { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; min-height: 88px; border-bottom: 1px solid var(--border); padding: 12px 0; } -.pairing-list output { color: var(--text); font: 700 25px/1 ui-monospace, monospace; letter-spacing: 0.12em; white-space: nowrap; } -.pairing-actions { display: flex; flex-wrap: wrap; gap: 12px; } - -.profile-dialog > header, .profile-dialog > footer { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; } -.profile-dialog > header { justify-content: space-between; } -.profile-dialog header p { margin-top: 4px; color: var(--muted); } -.profile-dialog > footer { margin-top: 20px; } -.profile-dialog > footer > span { flex: 1; } -.binding-list { margin-top: 18px; border-top: 1px solid var(--border); } +/* Each request is a card whose code is the thing to compare, so the code is + set large and spaced like the code on the phone. */ +.pairing-list { display: grid; gap: 12px; margin-top: 24px; } +.pairing-list article { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px 16px; border: 1px solid var(--border); border-radius: var(--radius-card); padding: 16px 20px; background: var(--surface-muted); } +.pairing-list h3 { overflow-wrap: anywhere; } +.pairing-list output { border-radius: 10px; padding: 8px 14px; color: var(--text); background: var(--surface); font: 700 28px/1 ui-monospace, Consolas, monospace; letter-spacing: 0.12em; white-space: nowrap; } +.pairing-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 12px; grid-column: 1 / -1; } + +.profile-dialog > header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; } +.binding-list { margin-top: 20px; border-top: 1px solid var(--border); } /* Switch label, action, then the action's value; both controls share the row instead of the action squeezing into a fixed narrow column. */ .profile-dialog { container-type: inline-size; } @@ -573,14 +588,20 @@ fieldset:disabled :is(.color-options, .scanner-colours) label:not(:has(input:che .binding-row > :nth-child(3):not(.binding-error) { grid-column: 2; } } -.capture-dialog { display: grid; justify-items: center; width: min(100%, 420px); border: 1px solid var(--brand-border); border-radius: 10px; padding: 32px 28px 24px; color: var(--text); background: var(--surface); box-shadow: var(--shadow-dialog); text-align: center; animation: capture-pulse 1.4s ease-in-out infinite; } +.switch-practice-dialog > p { max-width: 68ch; margin-top: 8px; color: var(--muted); } +.switch-practice-dialog > p[role="status"] { color: var(--text); font-weight: 600; } + +.capture-dialog { display: grid; justify-items: center; width: min(100%, 440px); border-color: var(--brand-border); padding: 36px 28px 28px; text-align: center; animation: capture-pulse 1.4s ease-in-out infinite; } .capture-dialog:focus { outline: none; } .capture-dialog > svg { color: var(--brand); } -.capture-dialog h2 { margin-top: 14px; } +.capture-dialog h2 { margin-top: 14px; font-size: 24px; line-height: 1.3; font-weight: 700; } .capture-dialog p { margin-top: 8px; color: var(--muted); } .capture-dialog .btn { margin-top: 20px; } @keyframes capture-pulse { 50% { border-color: var(--brand); box-shadow: var(--shadow-dialog), 0 0 0 4px var(--brand-container); } } -@media (prefers-reduced-motion: reduce) { .capture-dialog, .btn { animation: none; transition: none; } } +@media (prefers-reduced-motion: reduce) { + .setup-dialog, .pairing-dialog, .profile-dialog, .switch-practice-dialog, .capture-dialog { animation: none; } + .btn { transition: none; } +} @media (forced-colors: active) { .btn.nav-button[data-active="true"] { outline: 2px solid Highlight; outline-offset: -2px; } @@ -608,6 +629,7 @@ fieldset:disabled :is(.color-options, .scanner-colours) label:not(:has(input:che .update-banner .btn.text-button { grid-column: 2; justify-self: start; padding-left: 0; } } @media (max-width: 480px) { + .setup-dialog, .pairing-dialog, .profile-dialog, .switch-practice-dialog { padding: 20px; } .app-shell nav { grid-template-columns: repeat(2, minmax(0, 1fr)); } .view { padding: 12px; } .btn.profile-row { display: flex; flex-wrap: wrap; } @@ -617,7 +639,7 @@ fieldset:disabled :is(.color-options, .scanner-colours) label:not(:has(input:che .scanner-preferences .segmented { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-height: 600px) { - .setup-dialog { overflow-y: auto; } + .setup-dialog { min-height: 0; overflow-y: auto; } .setup-dialog > * { flex-shrink: 0; } .setup-dialog .setup-content { overflow: visible; } }