Landscape phone layout: compact donut, PWA support, safe-area insets

- Add compact donut mode for landscape phones (<=850px, landscape):
  wider radii (56%/93%/100%), no outside labels, bolder fonts
- Refine landscape CSS: smaller header (h1:20px), 55/45 chart/details
  split, compact month previews (22px), tighter spacing
- Add PWA standalone support: manifest.json, apple-mobile-web-app
  meta tags, viewport-fit=cover
- Add safe-area-inset padding for Dynamic Island and home indicator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Anton Volnuhin 2026-02-07 23:25:35 +03:00
parent cc8b10dadb
commit 029e428bb4
4 changed files with 78 additions and 22 deletions

24
app.js
View File

@ -2765,21 +2765,23 @@ function adjustChartSize() {
const screenWidth = window.innerWidth;
const isMobile = screenWidth <= 500;
const isLandscapePhone = screenWidth <= 850 && window.innerHeight < window.innerWidth;
const isCompact = isMobile || isLandscapePhone;
// Mobile: extend layers to fill container, keeping same hole size as desktop
// Compact mode (portrait phones + landscape phones): extend layers to fill container
// Hole stays at 20%, layers scaled to fill remaining 80% (vs 55% on desktop)
const level1Inner = '20%';
const level1Outer = isMobile ? '56%' : '45%';
const level2Outer = isMobile ? '93%' : '70%';
const level3Outer = isMobile ? '100%' : '75%';
const outerRadius = isMobile ? '100%' : '95%';
const level1Outer = isCompact ? '56%' : '45%';
const level2Outer = isCompact ? '93%' : '70%';
const level3Outer = isCompact ? '100%' : '75%';
const outerRadius = isCompact ? '100%' : '95%';
// Smaller font sizes on mobile, bolder for readability
const level1FontSize = isMobile ? 10 : 13;
const level1LineHeight = isMobile ? 12 : 15;
const level1FontWeight = isMobile ? 600 : 500;
const level2FontSize = isMobile ? 9 : 11;
const level2FontWeight = isMobile ? 600 : 400;
// Smaller font sizes on compact, bolder for readability
const level1FontSize = isCompact ? 10 : 13;
const level1LineHeight = isCompact ? 12 : 15;
const level1FontWeight = isCompact ? 600 : 500;
const level2FontSize = isCompact ? 9 : 11;
const level2FontWeight = isCompact ? 600 : 400;
// Update layer proportions
option.series.levels[1].r0 = level1Inner;

View File

@ -2,7 +2,11 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Траты">
<link rel="manifest" href="manifest.json">
<title>Spending Visualization</title>
<link rel="stylesheet" href="styles.css">
<script src="https://cdn.jsdelivr.net/npm/echarts@5.6.0/dist/echarts.min.js"></script>

9
manifest.json Normal file
View File

@ -0,0 +1,9 @@
{
"name": "Семейные траты",
"short_name": "Траты",
"start_url": "/",
"display": "standalone",
"background_color": "#f5f5f5",
"theme_color": "#f5f5f5",
"orientation": "any"
}

View File

@ -14,6 +14,7 @@ body {
width: 100%;
margin: 0 auto;
padding: 20px;
padding-top: calc(20px + env(safe-area-inset-top));
}
.header {
@ -613,6 +614,11 @@ body {
/* Landscape phones: revert to desktop-like layout */
@media (max-width: 850px) and (orientation: landscape) {
h1 {
font-size: 20px;
white-space: nowrap;
}
.view-switcher {
position: static;
background: none;
@ -622,6 +628,7 @@ body {
border-top: none;
display: inline-flex;
gap: 4px;
flex-shrink: 0;
}
.view-switcher-btn {
@ -630,6 +637,7 @@ body {
padding: 4px 8px;
border-radius: 0;
border-bottom: 2px solid transparent;
font-size: 13px;
font-weight: normal;
color: #999;
background: none;
@ -642,20 +650,44 @@ body {
}
.container {
padding: 6px 12px;
padding-bottom: 6px;
padding: 4px 10px;
padding-top: calc(4px + env(safe-area-inset-top));
padding-bottom: 4px;
padding-left: calc(10px + env(safe-area-inset-left));
padding-right: calc(10px + env(safe-area-inset-right));
}
.header {
flex-direction: row;
align-items: center;
gap: 12px;
margin-bottom: 4px;
gap: 8px;
margin-bottom: 2px;
}
.month-navigator {
max-width: 60%;
max-width: 55%;
width: auto;
flex-shrink: 1;
min-width: 0;
}
.month-btn {
padding: 2px 4px;
}
.month-preview {
width: 22px;
height: 22px;
}
.month-label {
font-size: 8px;
}
.nav-arrow {
width: 24px;
height: 24px;
font-size: 16px;
}
.content-wrapper {
@ -663,24 +695,33 @@ body {
}
.chart-wrapper {
width: 65%;
height: calc(100dvh - 50px);
width: 55%;
height: calc(100dvh - 38px);
}
#details-box {
position: relative;
top: 0;
right: 0;
width: 35%;
width: 45%;
min-width: auto;
margin-top: 0;
max-height: calc(100dvh - 50px);
max-height: calc(100dvh - 38px);
overflow-y: auto;
padding: 10px;
}
.container.timeline-mode .chart-wrapper {
width: 100%;
height: calc(100dvh - 50px);
height: calc(100dvh - 38px);
}
.details-header {
padding: 6px 0;
}
.top-item {
padding: 4px 0;
}
}