diff --git a/Untitled.png b/Untitled.png new file mode 100644 index 0000000..f0d36ad Binary files /dev/null and b/Untitled.png differ diff --git a/components/IconButton.client.vue b/components/IconButton.client.vue index c6343e9..8dd1c7b 100644 --- a/components/IconButton.client.vue +++ b/components/IconButton.client.vue @@ -8,6 +8,7 @@ :class="[ 'icon-button', { + 'xsmall': size === 'xsmall', 'large': size === 'large', 'small': size === 'small' } @@ -43,7 +44,7 @@ const props = defineProps({ size: { type: String, default: 'normal', - validator: (value) => ['small', 'normal', 'large'].includes(value) + validator: (value) => ['xsmall', 'small', 'normal', 'large'].includes(value) }, badge: { type: [String, Number], @@ -55,6 +56,7 @@ defineEmits(['click']) const iconSize = computed(() => { switch (props.size) { + case 'xsmall': return 16 case 'small': return 18 case 'large': return 28 default: return 20 @@ -67,6 +69,12 @@ const iconSize = computed(() => { position: relative; } +.icon-button.xsmall { + width: 28px; + height: 28px; + font-size: 0.8rem; +} + .icon-button.small { width: 36px; height: 36px; @@ -135,4 +143,4 @@ const iconSize = computed(() => { 50% { transform: scale(1.2); } 100% { transform: scale(1); } } - \ No newline at end of file + diff --git a/components/MainContainer.client.vue b/components/MainContainer.client.vue index 8fe8897..f4eb65e 100644 --- a/components/MainContainer.client.vue +++ b/components/MainContainer.client.vue @@ -58,7 +58,7 @@ const hasActiveTrack = computed(() => !!props.currentTrack) .main-container { /* Parametrized viewport sizing */ --app-vertical-margin: 10px; /* Top/bottom margin between both components */ - --playback-controls-height: 60px; /* Adjustable height for PlaybackControls */ + --playback-controls-height: 40px; /* Compact but not tiny */ --tracklist-height: calc(100vh - (var(--app-vertical-margin) * 2) - var(--playback-controls-height)); /* Container takes full viewport minus vertical margins */ @@ -109,10 +109,13 @@ const hasActiveTrack = computed(() => !!props.currentTrack) display: flex; justify-content: space-between; align-items: center; + flex-wrap: nowrap; + column-gap: 12px; } .app-title { - flex: 1; + flex: 1 1 auto; + min-width: 0; /* allow text ellipsis */ } .title-text { @@ -127,6 +130,9 @@ const hasActiveTrack = computed(() => !!props.currentTrack) display: flex; align-items: center; gap: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .title-icon { @@ -144,8 +150,16 @@ const hasActiveTrack = computed(() => !!props.currentTrack) .header-controls { display: flex; - gap: 16px; + gap: 10px; align-items: center; + flex: 0 0 auto; +} + +/* Avoid hover transforms causing layout shifts in header controls */ +.header-controls :deep(.base-button.icon:hover), +.header-controls :deep(.theme-toggle:hover), +.header-controls :deep(.icon-button:hover .icon) { + transform: none !important; } .main-content { @@ -192,7 +206,14 @@ const hasActiveTrack = computed(() => !!props.currentTrack) } /* Responsive Design */ -@media (max-width: 768px) { +@media (max-width: 680px) { + /* Hide subtitle to keep header inline on narrow screens */ + .subtitle { + display: none; + } +} + +@media (max-width: 520px) { .main-container { padding: 15px; padding-bottom: 0; @@ -202,8 +223,8 @@ const hasActiveTrack = computed(() => !!props.currentTrack) .header-content { flex-direction: column; - gap: 16px; - padding: 16px; + gap: 12px; + padding: 12px 16px; text-align: center; } diff --git a/components/PlaybackControls.client.vue b/components/PlaybackControls.client.vue index 9f1080b..af0c404 100644 --- a/components/PlaybackControls.client.vue +++ b/components/PlaybackControls.client.vue @@ -68,8 +68,10 @@ const cycleRepeat = () => { /* Allow parent to control overall height budget */ height: var(--playback-controls-height, auto); display: flex; - gap: 10px; + gap: 4px; align-items: center; + flex-wrap: nowrap; + white-space: nowrap; } .shuffle-btn:hover { @@ -97,10 +99,18 @@ const cycleRepeat = () => { box-shadow: 0 0 20px var(--accent-primary); } +/* Keep layout from expanding on hover transforms */ +.playback-controls :deep(.base-button.icon:hover) { + transform: none !important; +} +.playback-controls :deep(.icon-button:hover .icon) { + transform: none !important; +} + /* Responsive design */ @media (max-width: 768px) { .playback-controls { - gap: 8px; + gap: 4px; } }