feat: make the web UI usable on phones (360-768px)
Responsive pass limited to CSS plus small markup hooks, so desktop (>900px) is unchanged. - shell: static header with 2-row controls, sticky scrollable tab bar with fade + snap, 40px touch targets, 16px inputs, full-width toasts with safe-area - grids collapse to one column; dashboard counters become 3 compact tiles and analyst stats 2x2 - equipment: full-width subtabs/filters, 3-up tiles, inspector becomes a bottom sheet on phones - inventory: 2 columns fit without side scrolling, full-width quantity inputs - characters: full-width search, 2-column avatar grid with stacked portrait - dialogs: dvh-capped bottom sheets, two-column light cone and avatar pickers - json/monster/settings/console form and scroll fixes - compact one-row header for landscape phones (844x390 174px -> 108px) - i18n: localize inventory view and relic-set search aria labels/placeholder - bump styles.css 2.33, item-catalog.css 3, app.js 3.27, i18n.js 2.22
This commit is contained in:
1 parent
45d8414e60
commit
992875348d
9 files changed
+497
-14
No files matched your search
@@ -107,6 +107,10 @@
|
||||
@media (max-width: 540px) {
|
||||
.item-catalog-grid { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
|
||||
.item-catalog-toolbar .form-select { flex: 1 1 100%; }
|
||||
/* Keep supporting text legible on phones. */
|
||||
.item-catalog-name { font-size: .88rem; }
|
||||
.item-catalog-details { font-size: .72rem; }
|
||||
.item-catalog-support { font-size: .68rem; }
|
||||
}
|
||||
.inventory-mode-switch {
|
||||
display: flex;
|
||||
|
||||
+471
@@ -4499,3 +4499,474 @@ input:checked + .slider:before {
|
||||
.substat-option:hover:not(:disabled), .substat-option:focus-visible { background: #263d59; }
|
||||
.substat-option:disabled { opacity: .4; cursor: not-allowed; }
|
||||
@media (max-width: 600px) { .add-relic-set-grid { grid-template-columns: repeat(auto-fill, minmax(105px, 1fr)); max-height: 230px; } }
|
||||
|
||||
/* ==========================================================================
|
||||
Mobile / responsive pass
|
||||
Breakpoints: 900 (tablet), 768 (large phone), 640 (phone), 480 (small
|
||||
phone), 360 (compact). Desktop layout above 900px is left untouched.
|
||||
========================================================================== */
|
||||
|
||||
/* --- Shell: header, tab bar, page padding ------------------------------- */
|
||||
@media (max-width: 768px) {
|
||||
/* The tab bar becomes the sticky element so phones keep vertical space
|
||||
for content instead of pinning a tall header. */
|
||||
.app-header {
|
||||
position: static;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.6rem;
|
||||
padding: 0.6rem 0.85rem;
|
||||
}
|
||||
|
||||
.brand { min-width: 0; }
|
||||
.brand-logo-img { width: 36px; height: 36px; }
|
||||
.brand-title { font-size: 1.1rem; }
|
||||
.brand-subtitle { font-size: 0.62rem; }
|
||||
|
||||
.header-controls {
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.server-mode-badge,
|
||||
.connection-pill {
|
||||
min-height: 36px;
|
||||
padding: 0.32rem 0.6rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* Two tidy rows on phones: [server mode] [status] / [language] [refresh] */
|
||||
.server-mode-badge { order: 1; }
|
||||
.connection-pill { order: 2; }
|
||||
#lang-select {
|
||||
order: 3;
|
||||
width: auto;
|
||||
max-width: 46vw;
|
||||
min-height: 36px;
|
||||
padding: 0.3rem 0.6rem;
|
||||
}
|
||||
#btn-quick-ping { order: 4; }
|
||||
|
||||
.nav-tabs {
|
||||
top: 0;
|
||||
padding: 0.4rem 0.55rem;
|
||||
gap: 0.25rem;
|
||||
scroll-snap-type: x proximity;
|
||||
scrollbar-width: none;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
mask-image: linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
|
||||
-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
|
||||
}
|
||||
|
||||
.nav-tabs::-webkit-scrollbar { display: none; }
|
||||
|
||||
.tab-btn {
|
||||
min-height: 40px;
|
||||
gap: 0.35rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
|
||||
.main-container { padding: 0.9rem 0.8rem 1.4rem; }
|
||||
|
||||
/* Touch targets */
|
||||
.btn { min-height: 40px; }
|
||||
.btn-sm { min-height: 36px; }
|
||||
|
||||
/* iOS zooms the page when a focused field is smaller than 16px. */
|
||||
input,
|
||||
select,
|
||||
textarea { font-size: 16px; }
|
||||
|
||||
.form-input,
|
||||
.form-select {
|
||||
min-height: 44px;
|
||||
padding: 0.6rem 0.75rem;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.form-label { font-size: 0.72rem; }
|
||||
|
||||
/* Toasts: keep clear of screen edges and the home indicator. */
|
||||
.toast-container {
|
||||
left: 0.75rem;
|
||||
right: 0.75rem;
|
||||
bottom: calc(0.9rem + env(safe-area-inset-bottom, 0px));
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.toast {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.brand-subtitle { display: none; }
|
||||
|
||||
.header-controls { gap: 0.4rem; }
|
||||
|
||||
.tab-btn {
|
||||
padding: 0.45rem 0.65rem;
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Layout grids ------------------------------------------------------- */
|
||||
/* The desktop grids use fixed minmax floors (320/280/250px) which no longer
|
||||
fit phone content boxes; allow them to collapse instead of overflowing.
|
||||
Page-level grids collapse to one column, while field grids inside dialogs
|
||||
may still show two compact columns. */
|
||||
@media (max-width: 640px) {
|
||||
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
|
||||
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
|
||||
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); }
|
||||
|
||||
.modal-content .grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)); }
|
||||
|
||||
/* Dashboard counters: three compact tiles instead of three full-width cards. */
|
||||
#tab-dashboard > .grid-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
#tab-dashboard > .grid-3 .stat-box {
|
||||
padding: 0.65rem 0.6rem;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
#tab-dashboard > .grid-3 .stat-number { font-size: 1.55rem; }
|
||||
|
||||
#tab-dashboard > .grid-3 .stat-label {
|
||||
font-size: 0.66rem;
|
||||
line-height: 1.25;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Dialogs as phone bottom sheets ------------------------------------- */
|
||||
@media (max-width: 640px) {
|
||||
.modal-overlay {
|
||||
padding: 0;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
max-height: calc(100dvh - 0.5rem);
|
||||
padding: 1rem 0.9rem calc(1rem + env(safe-area-inset-bottom, 0px));
|
||||
border-width: 1px 0 0;
|
||||
border-radius: 18px 18px 0 0;
|
||||
}
|
||||
|
||||
.modal-content .modal-header {
|
||||
margin-bottom: 0.9rem;
|
||||
padding-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.assign-picker-modal {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
max-height: calc(100dvh - 0.5rem) !important;
|
||||
border-radius: 18px 18px 0 0 !important;
|
||||
}
|
||||
|
||||
.modal-content .grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)); }
|
||||
}
|
||||
|
||||
/* --- Equipment: tiles, subtabs, inspector ------------------------------- */
|
||||
@media (max-width: 900px) {
|
||||
/* The side inspector was a 320px sticky column; on narrow screens it moves
|
||||
under the tile grid instead of squeezing it to nothing. */
|
||||
.eq-layout { flex-wrap: wrap; }
|
||||
#eq-inspector-card {
|
||||
width: 100% !important;
|
||||
position: static !important;
|
||||
}
|
||||
.eq-tiles-grid { max-height: 50vh; }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
/* The server-mode <select> shrinks to the text height; give it a real
|
||||
touch target inside its pill. */
|
||||
#server-mode-select {
|
||||
min-height: 32px;
|
||||
padding-block: 0.2rem;
|
||||
}
|
||||
|
||||
/* Subtabs and filters each take their own row. */
|
||||
.eq-toolbar-subtabs { width: 100%; }
|
||||
.eq-toolbar-subtabs .eq-subtab-btn {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.eq-toolbar-subtabs .eq-subtab-btn > span:first-child {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#eq-search {
|
||||
min-width: 0 !important;
|
||||
flex: 0 0 100% !important;
|
||||
}
|
||||
|
||||
.eq-toolbar-filters {
|
||||
flex: 0 0 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
#eq-filter-rarity,
|
||||
#eq-filter-equipped,
|
||||
#eq-filter-extra {
|
||||
max-width: none !important;
|
||||
min-width: 0;
|
||||
flex: 1 1 44%;
|
||||
}
|
||||
|
||||
.eq-tiles-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
|
||||
gap: 0.55rem;
|
||||
}
|
||||
|
||||
.eq-tile-name-snippet { max-width: none; }
|
||||
|
||||
/* Phones: the inspector becomes a bottom sheet so details stay in reach
|
||||
right after tapping a tile (it already ships its own close button). */
|
||||
#eq-inspector-card {
|
||||
position: fixed !important;
|
||||
top: auto !important;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100% !important;
|
||||
max-height: 78dvh;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
z-index: 900;
|
||||
border-width: 1px 0 0;
|
||||
border-radius: 16px 16px 0 0;
|
||||
padding-bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
|
||||
box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Inventory: keep the two columns readable without side scrolling ---- */
|
||||
@media (max-width: 640px) {
|
||||
#inventory-manage-panel table { min-width: 100% !important; }
|
||||
#inventory-manage-panel th,
|
||||
#inventory-manage-panel td { padding-inline: 0.7rem !important; }
|
||||
.inventory-count-input {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
#inventory-search {
|
||||
min-width: 0 !important;
|
||||
flex: 0 0 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Characters: filters and avatar grid -------------------------------- */
|
||||
@media (max-width: 640px) {
|
||||
#avatar-search {
|
||||
min-width: 0 !important;
|
||||
flex: 0 0 100% !important;
|
||||
}
|
||||
|
||||
#avatar-filter-rarity,
|
||||
#avatar-filter-element,
|
||||
#avatar-filter-path {
|
||||
max-width: none !important;
|
||||
flex: 1 1 44%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#avatars-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.65rem;
|
||||
}
|
||||
|
||||
/* Two columns are only ~150px wide, so stack the portrait above the name
|
||||
instead of squeezing the text next to it. */
|
||||
#avatars-grid .avatar-card {
|
||||
padding: 0.7rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#avatars-grid .avatar-header-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.45rem;
|
||||
margin-right: 0;
|
||||
padding-top: 0.3rem;
|
||||
}
|
||||
|
||||
#avatars-grid .avatar-image-container {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
#avatars-grid .avatar-name {
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.3;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
#avatars-grid .avatar-subinfo { margin-top: 0.2rem; }
|
||||
#avatars-grid .avatar-subinfo .icon-badge { width: 22px; height: 22px; }
|
||||
#avatars-grid .avatar-card-footer {
|
||||
margin-top: 0.55rem;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Modals: phone tuning ---------------------------------------------- */
|
||||
@media (max-width: 640px) {
|
||||
/* Several modals carry an inline 92vh cap; dvh keeps them above the
|
||||
mobile URL bar / keyboard. */
|
||||
.modal-content { max-height: calc(100dvh - 0.5rem) !important; }
|
||||
|
||||
/* The avatar detail modal is wider/taller by an id-scoped desktop rule. */
|
||||
#modal-avatar-detail .modal-content {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
max-height: calc(100dvh - 0.5rem) !important;
|
||||
}
|
||||
|
||||
/* Its stat grid has an inline 320px floor that is wider than the sheet. */
|
||||
#modal-avatar-detail .grid-2 {
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)) !important;
|
||||
}
|
||||
|
||||
/* Light-cone pickers: two artwork columns instead of one. */
|
||||
#add-lc-visual-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 140px), 1fr)) !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
|
||||
#lc-picker-list {
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 150px), 1fr)) !important;
|
||||
}
|
||||
|
||||
/* Avatar / relic-slot pickers: two compact tiles per row. */
|
||||
.picker-grid-container {
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 160px), 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* --- JSON editor -------------------------------------------------------- */
|
||||
@media (max-width: 640px) {
|
||||
#tab-json .card-title > div { flex-wrap: wrap; }
|
||||
.json-textarea {
|
||||
height: 60vh;
|
||||
min-height: 240px;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.json-editor-wrapper > div:last-child {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Monster settings / analyst / console ------------------------------ */
|
||||
@media (max-width: 640px) {
|
||||
.monster-aa-round-control { grid-template-columns: 1fr; gap: 0.85rem; }
|
||||
|
||||
.monster-settings-tabs {
|
||||
scroll-snap-type: x proximity;
|
||||
scrollbar-width: none;
|
||||
mask-image: linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
|
||||
-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
|
||||
}
|
||||
.monster-settings-tabs::-webkit-scrollbar { display: none; }
|
||||
.monster-settings-tab { scroll-snap-align: start; }
|
||||
|
||||
/* Analyst: stat tiles read as 2x2; the character cards already stack. */
|
||||
#tab-damage-analyst > .grid-4 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.65rem;
|
||||
}
|
||||
#tab-damage-analyst > .grid-4 .card { padding: 0.85rem !important; }
|
||||
#tab-damage-analyst > .grid-4 .card > div:nth-child(2) {
|
||||
font-size: 1.4rem !important;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.console-terminal-log {
|
||||
height: 190px;
|
||||
max-height: 45vh;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Settings rows ------------------------------------------------------ */
|
||||
@media (max-width: 768px) {
|
||||
/* Long server paths must break, and toggle rows must keep a full switch. */
|
||||
#tab-settings .card div { overflow-wrap: anywhere; }
|
||||
#tab-settings .switch { flex: 0 0 auto; }
|
||||
}
|
||||
|
||||
/* --- Landscape phones --------------------------------------------------- */
|
||||
/* Wide but short screens would otherwise stack a 100px header and a 73px tab
|
||||
bar, leaving little room for the page. */
|
||||
@media (max-height: 500px) and (orientation: landscape) and (min-width: 700px) {
|
||||
.app-header {
|
||||
position: static;
|
||||
padding: 0.4rem 0.9rem;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.brand-logo-img { width: 30px; height: 30px; }
|
||||
.brand-title { font-size: 0.95rem; }
|
||||
.brand-subtitle { display: none; }
|
||||
|
||||
.header-controls {
|
||||
width: auto;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.server-mode-badge,
|
||||
.connection-pill {
|
||||
min-height: 32px;
|
||||
padding: 0.22rem 0.5rem;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
#server-mode-select { min-height: 28px; }
|
||||
|
||||
#lang-select {
|
||||
width: auto;
|
||||
min-height: 32px;
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
#btn-quick-ping {
|
||||
min-height: 32px;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
top: 0;
|
||||
padding: 0.25rem 0.6rem;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
min-height: 34px;
|
||||
padding: 0.3rem 0.65rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.main-container { padding: 0.7rem 0.8rem 1.2rem; }
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user