feat(dashboard): improve widget scaling and fix attribute scrollbar

Implement responsive scaling for info widgets and fix sizing issues:

**1. Container-Responsive Info Widgets (style.css)**

**Calendar Widget:**
- Add flexbox layout (height: 100%, flex-direction: column)
- Change font sizes from vw to rem for better scaling
- Calendar day now uses clamp(1.5rem, 2.5rem, 3.5rem) to fill space
- Add flex-shrink: 0 to top/year, flex: 1 to day

**Weather Widget:**
- Add container wrapper (height: 100%, justify-content: space-around)
- Weather icon scales with container: clamp(2rem, 8vh, 4rem)
- Forecast text uses rem instead of vw
- Both elements marked flex-shrink: 0

**Temperature Widget:**
- Container fills height with flexbox centering
- Thermometer scales: clamp(4rem, 60%, 8rem) height
- Tube/bulb use percentages (40% width, 70% height)
- Text value uses rem units

**Clock Widget:**
- Container with space-around layout
- Clock scales with container: clamp(3rem, 60%, 6rem)
- Clock hands use percentages of clock size
- Time text uses rem units

**Location Widget:**
- Container flexbox with column layout
- Map background uses flex: 1 (was fixed 1.875rem)
- Map marker scales: clamp(1.5rem, 4vh, 3rem)
- Location text uses rem units

**2. Fix Attributes Widget Scrollbar (style.css)**
- Line 966: Change grid-auto-rows: 1fr to grid-auto-rows: minmax(0, 1fr)
- Allows rows to shrink below natural size to fit container
- Prevents overflow when widget manually positioned after auto-arrange

**3. Widget Size Constraints (widget files)**
- userAttributesWidget.js: Change minSize from {w:1, h:2} to {w:2, h:2}
  - Enforces 2x2 minimum as requested
  - Prevents cramped 1-column layout
- infoBoxWidgets.js: Change location minSize from {w:2, h:2} to {w:1, h:2}
  - Allows narrow 1x2 layout for space-constrained dashboards
  - Only widget that didn't fit on desktop screen

**Technical Details:**
- All info widgets now use rem units instead of vw for text
- Flexbox scaling ensures widgets fill their containers beautifully
- Percentage-based sizing for thermometer/clock internal elements
- clamp() used for min/preferred/max sizing across resolutions
- minmax(0, 1fr) fixes classic CSS grid overflow issue

**User-Reported Issues Fixed:**
 Info widgets scale to fill containers instead of fixed sizes
 Attributes widget no longer shows scrollbar in 2x2 (manual or auto-arranged)
 Location widget works in both 1x2 and 2x2 layouts
 All widgets maintain readability across different panel widths

Related: Dashboard v2, Epic 2, Phase 3.2
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-23 18:33:01 +11:00
parent b3a86d4609
commit 5dd7dcb27b
3 changed files with 71 additions and 37 deletions
@@ -401,7 +401,7 @@ export function registerLocationWidget(registry, dependencies) {
name: 'Location', name: 'Location',
icon: '📍', icon: '📍',
description: 'Map with location display', description: 'Map with location display',
minSize: { w: 2, h: 2 }, minSize: { w: 1, h: 2 },
defaultSize: { w: 2, h: 2 }, defaultSize: { w: 2, h: 2 },
requiresSchema: false, requiresSchema: false,
@@ -31,7 +31,7 @@ export function registerUserAttributesWidget(registry, dependencies) {
icon: '⚔️', icon: '⚔️',
description: 'Classic RPG stats (STR, DEX, CON, INT, WIS, CHA)', description: 'Classic RPG stats (STR, DEX, CON, INT, WIS, CHA)',
category: 'user', category: 'user',
minSize: { w: 1, h: 2 }, minSize: { w: 2, h: 2 },
defaultSize: { w: 2, h: 2 }, defaultSize: { w: 2, h: 2 },
requiresSchema: false, requiresSchema: false,
+69 -35
View File
@@ -963,7 +963,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: clamp(2px, 0.4vh, 4px); gap: clamp(2px, 0.4vh, 4px);
flex: 1; flex: 1;
grid-auto-rows: 1fr; grid-auto-rows: minmax(0, 1fr);
min-height: 0; min-height: 0;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
@@ -1369,31 +1369,38 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
box-shadow: 0 4px 12px var(--rpg-shadow); box-shadow: 0 4px 12px var(--rpg-shadow);
} }
/* Location widget - flexible height */ /* Location Widget */
.rpg-location-widget { .rpg-location-widget {
height: 100%; height: 100%;
display: flex;
flex-direction: column;
padding: 0.25rem;
} }
/* Calendar Widget */ /* Calendar Widget */
.rpg-calendar-widget { .rpg-calendar-widget {
padding: 0.188em; padding: 0.188em;
height: 100%;
display: flex;
flex-direction: column;
} }
.rpg-calendar-top { .rpg-calendar-top {
background: var(--rpg-highlight); background: var(--rpg-highlight);
color: var(--rpg-bg); color: var(--rpg-bg);
font-size: clamp(0.5vw, 0.55vw, 0.6vw); font-size: clamp(0.6rem, 0.7rem, 0.8rem);
font-weight: bold; font-weight: bold;
padding: 0.125em 0.375em; padding: 0.125em 0.375em;
border-radius: 3px 3px 0 0; border-radius: 3px 3px 0 0;
width: 100%; width: 100%;
text-align: center; text-align: center;
flex-shrink: 0;
} }
.rpg-calendar-day { .rpg-calendar-day {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
color: var(--rpg-text); color: var(--rpg-text);
font-size: clamp(0.8vw, 1vw, 1.2vw); font-size: clamp(1.5rem, 2.5rem, 3.5rem);
font-weight: bold; font-weight: bold;
padding: 0.25em; padding: 0.25em;
width: 100%; width: 100%;
@@ -1404,24 +1411,35 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
min-height: 0;
} }
.rpg-calendar-year { .rpg-calendar-year {
font-size: clamp(0.5vw, 0.55vw, 0.6vw); font-size: clamp(0.6rem, 0.7rem, 0.8rem);
color: var(--rpg-text); color: var(--rpg-text);
opacity: 0.7; opacity: 0.7;
margin-top: 0.062em; margin-top: 0.062em;
flex-shrink: 0;
}
/* Weather Widget */
.rpg-weather-widget {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
padding: 0.5rem;
} }
/* Weather Widget Icon */
.rpg-weather-icon { .rpg-weather-icon {
font-size: clamp(18px, 3.5vw, 24px); font-size: clamp(2rem, 8vh, 4rem);
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)); filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
flex-shrink: 0; flex-shrink: 0;
} }
.rpg-weather-forecast { .rpg-weather-forecast {
font-size: clamp(0.4vw, 0.5vw, 0.6vw); font-size: clamp(0.7rem, 0.9rem, 1.1rem);
text-align: center; text-align: center;
margin: 0; margin: 0;
font-weight: 600; font-weight: 600;
@@ -1433,6 +1451,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
max-width: 100%; max-width: 100%;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
flex-shrink: 0;
} }
.rpg-weather-forecast.rpg-editable { .rpg-weather-forecast.rpg-editable {
@@ -1441,22 +1460,28 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
/* Temperature Widget - Thermometer */ /* Temperature Widget - Thermometer */
.rpg-temp-widget { .rpg-temp-widget {
gap: 0.188em; height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.5rem;
} }
.rpg-thermometer { .rpg-thermometer {
position: relative; position: relative;
width: 1.25rem; width: clamp(1.5rem, 2rem, 3rem);
height: 2.5rem; height: clamp(4rem, 60%, 8rem);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
flex-shrink: 1;
} }
.rpg-thermometer-tube { .rpg-thermometer-tube {
position: relative; position: relative;
width: 0.5rem; width: 40%;
height: 1.75rem; height: 70%;
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
border: 2px solid var(--rpg-border); border: 2px solid var(--rpg-border);
border-radius: 0.625em 0.625em 0 0; border-radius: 0.625em 0.625em 0 0;
@@ -1475,8 +1500,9 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-thermometer-bulb { .rpg-thermometer-bulb {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
width: 0.875rem; width: 70%;
height: 0.875rem; height: 0;
padding-bottom: 70%;
background: var(--rpg-highlight); background: var(--rpg-highlight);
border: 2px solid var(--rpg-border); border: 2px solid var(--rpg-border);
border-radius: 50%; border-radius: 50%;
@@ -1484,25 +1510,33 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
} }
.rpg-temp-value { .rpg-temp-value {
font-size: clamp(0.5vw, 0.6vw, 0.7vw); font-size: clamp(0.7rem, 0.9rem, 1.1rem);
font-weight: bold; font-weight: bold;
color: var(--rpg-text); color: var(--rpg-text);
text-align: center; text-align: center;
flex-shrink: 0;
} }
/* Clock Widget */ /* Clock Widget */
.rpg-clock-widget { .rpg-clock-widget {
gap: 0.188em; height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
gap: 0.5rem;
} }
.rpg-clock { .rpg-clock {
width: 2.625rem; width: clamp(3rem, 60%, 6rem);
height: 2.625rem; height: clamp(3rem, 60%, 6rem);
aspect-ratio: 1 / 1;
border-radius: 50%; border-radius: 50%;
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.4);
border: 3px solid var(--rpg-border); border: 3px solid var(--rpg-border);
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5); box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
position: relative; position: relative;
flex-shrink: 1;
} }
.rpg-clock-face { .rpg-clock-face {
@@ -1522,22 +1556,22 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
} }
.rpg-clock-hour { .rpg-clock-hour {
width: 0.188rem; width: 3%;
height: 0.75rem; height: 28%;
margin-left: -0.094em; margin-left: -1.5%;
opacity: 0.9; opacity: 0.9;
} }
.rpg-clock-minute { .rpg-clock-minute {
width: 0.125rem; width: 2%;
height: 1rem; height: 38%;
margin-left: -0.062em; margin-left: -1%;
} }
.rpg-clock-center { .rpg-clock-center {
position: absolute; position: absolute;
width: 0.312rem; width: 6%;
height: 0.312rem; height: 6%;
background: var(--rpg-highlight); background: var(--rpg-highlight);
border-radius: 50%; border-radius: 50%;
top: 50%; top: 50%;
@@ -1547,17 +1581,18 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
} }
.rpg-time-value { .rpg-time-value {
font-size: clamp(0.5vw,0.6vw,0.7vw); font-size: clamp(0.7rem, 0.9rem, 1.1rem);
font-weight: bold; font-weight: bold;
color: var(--rpg-text); color: var(--rpg-text);
flex-shrink: 0;
} }
/* Location Widget - Map */ /* Location Widget - Map */
.rpg-map-bg { .rpg-map-bg {
width: 100%; width: 100%;
height: 1.875rem; flex: 1;
min-height: 3rem;
margin: 0; margin: 0;
margin-bottom: 0 !important;
background: background:
linear-gradient(45deg, rgba(255,255,255,0.05) 25%, transparent 25%), linear-gradient(45deg, rgba(255,255,255,0.05) 25%, transparent 25%),
linear-gradient(-45deg, rgba(255,255,255,0.05) 25%, transparent 25%), linear-gradient(-45deg, rgba(255,255,255,0.05) 25%, transparent 25%),
@@ -1573,12 +1608,10 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
justify-content: center; justify-content: center;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
flex-shrink: 0;
margin-bottom: 0.188em;
} }
.rpg-map-marker { .rpg-map-marker {
font-size: 1vw; font-size: clamp(1.5rem, 4vh, 3rem);
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.8)); filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.8));
animation: markerPulse 2s ease-in-out infinite; animation: markerPulse 2s ease-in-out infinite;
} }
@@ -1589,16 +1622,17 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
} }
.rpg-location-text { .rpg-location-text {
font-size: clamp(0.5vw, 0.6vw, 0.7vw); font-size: clamp(0.7rem, 0.9rem, 1.1rem);
font-weight: bold; font-weight: bold;
color: var(--rpg-text); color: var(--rpg-text);
text-align: center; text-align: center;
line-height: 1.2; line-height: 1.2;
padding: 0.125em 0.25em; padding: 0.5rem 0.25rem;
margin: 0; margin: 0;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
hyphens: auto; hyphens: auto;
flex-shrink: 0;
} }
/* Character Status Cards */ /* Character Status Cards */