my website at ewancroft.uk

refactor(cards): clean up word-breaking and layout consistency

ewancroft.uk f4320b26 491fff27

verified
Changed files
+91 -89
src
lib
components
+90 -88
src/lib/components/layout/main/card/MusicStatusCard.svelte
··· 75 75 {@const safeMusicStatus = musicStatus} 76 76 <Card variant="elevated" padding="md"> 77 77 {#snippet children()} 78 - <div class="flex items-start gap-3"> 79 - <!-- Artwork --> 80 - <div class="shrink-0"> 81 - {#if safeMusicStatus.artworkUrl && !artworkError} 82 - <img 83 - src={safeMusicStatus.artworkUrl} 84 - alt="Album artwork for {safeMusicStatus.releaseName || safeMusicStatus.trackName}" 85 - class="h-20 w-20 rounded-lg object-cover shadow-md" 86 - loading="lazy" 87 - onerror={handleImageError} 88 - /> 89 - {:else} 90 - <div 91 - class="flex h-20 w-20 items-center justify-center rounded-lg bg-canvas-200 shadow-md dark:bg-canvas-700" 92 - > 93 - <Disc3 class="h-10 w-10 text-ink-500 dark:text-ink-400" aria-hidden="true" /> 94 - </div> 95 - {/if} 78 + <div> 79 + <!-- Header (Now Listening / Last Played) --> 80 + <div class="mb-4 flex items-center gap-2"> 81 + <Music class="h-4 w-4 text-primary-600 dark:text-primary-400" aria-hidden="true" /> 82 + <span 83 + class="text-xs font-semibold tracking-wide text-ink-800 uppercase dark:text-ink-100" 84 + > 85 + {safeMusicStatus.$type === 'fm.teal.alpha.actor.status' 86 + ? 'Now Listening' 87 + : 'Last Played'} 88 + </span> 96 89 </div> 97 90 98 - <!-- Info --> 99 - <div class="min-w-0 flex-1"> 100 - <!-- Header (Now Listening / Last Played) --> 101 - <div class="mb-4 flex items-center gap-2"> 102 - <Music class="h-4 w-4 text-primary-600 dark:text-primary-400" aria-hidden="true" /> 103 - <span 104 - class="text-xs font-semibold tracking-wide text-ink-800 uppercase dark:text-ink-100" 105 - > 106 - {safeMusicStatus.$type === 'fm.teal.alpha.actor.status' 107 - ? 'Now Listening' 108 - : 'Last Played'} 109 - </span> 110 - </div> 111 - 112 - <!-- Content --> 113 - <div class="mb-4"> 114 - <!-- Track Name --> 115 - <a 116 - href={safeMusicStatus.originUrl || '#'} 117 - target="_blank" 118 - rel="noopener noreferrer" 119 - class="block max-w-full text-lg font-semibold wrap-break-word whitespace-normal text-primary-600 transition-colors hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300" 120 - class:pointer-events-none={!safeMusicStatus.originUrl} 121 - class:cursor-default={!safeMusicStatus.originUrl} 122 - class:opacity-70={!safeMusicStatus.originUrl} 123 - > 124 - {safeMusicStatus.trackName} 125 - </a> 126 - 127 - <!-- Artists --> 128 - <p 129 - class="mt-1 flex max-w-full items-start gap-1.5 text-base wrap-break-word whitespace-normal text-ink-800 dark:text-ink-100" 130 - > 131 - <Users class="mt-0.5 h-4 w-4 flex-shrink-0 text-ink-600 dark:text-ink-300" /> 132 - {formatArtists(safeMusicStatus.artists)} 133 - </p> 134 - 135 - <!-- Album + Duration --> 136 - {#if safeMusicStatus.releaseName} 137 - <p 138 - class="mt-1 flex max-w-full items-start gap-1.5 text-sm wrap-break-word whitespace-normal text-ink-700 dark:text-ink-200" 91 + <div class="flex items-start gap-3"> 92 + <!-- Artwork --> 93 + <div class="shrink-0"> 94 + {#if safeMusicStatus.artworkUrl && !artworkError} 95 + <img 96 + src={safeMusicStatus.artworkUrl} 97 + alt="Album artwork for {safeMusicStatus.releaseName || safeMusicStatus.trackName}" 98 + class="h-20 w-20 rounded-lg object-cover shadow-md" 99 + loading="lazy" 100 + onerror={handleImageError} 101 + /> 102 + {:else} 103 + <div 104 + class="flex h-20 w-20 items-center justify-center rounded-lg bg-canvas-200 shadow-md dark:bg-canvas-700" 139 105 > 140 - <Album class="mt-0.5 h-4 w-4 flex-shrink-0 text-ink-500 dark:text-ink-400" /> 141 - <span> 142 - {safeMusicStatus.releaseName} 143 - 144 - {#if safeMusicStatus.duration} 145 - <span 146 - class="ml-1 inline-flex items-center gap-1 text-ink-600 dark:text-ink-300" 147 - > 148 - · <Clock class="h-3 w-3" /> 149 - {formatDuration(safeMusicStatus.duration)} 150 - </span> 151 - {/if} 152 - </span> 153 - </p> 106 + <Disc3 class="h-10 w-10 text-ink-500 dark:text-ink-400" aria-hidden="true" /> 107 + </div> 154 108 {/if} 155 109 </div> 156 110 157 - <!-- Footer / Meta --> 158 - <div class="flex items-center gap-2 text-xs text-ink-700 dark:text-ink-200"> 159 - <time datetime={safeMusicStatus.playedTime}> 160 - {formatRelativeTime(safeMusicStatus.playedTime)} 161 - </time> 162 - 163 - {#if safeMusicStatus.musicServiceBaseDomain} 164 - <span class="text-ink-600 dark:text-ink-300">·</span> 165 - 111 + <!-- Info --> 112 + <div class="min-w-0 flex-1"> 113 + <!-- Content --> 114 + <div class="mb-4"> 115 + <!-- Track Name --> 166 116 <a 167 - href="https://teal.fm" 117 + href={safeMusicStatus.originUrl || '#'} 168 118 target="_blank" 169 119 rel="noopener noreferrer" 170 - class="inline-flex items-center gap-1 transition-colors hover:text-primary-600 dark:hover:text-primary-400" 171 - title="Powered by teal.fm" 120 + class="block max-w-full text-lg font-semibold wrap-break-word whitespace-normal text-primary-600 transition-colors hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300" 121 + class:pointer-events-none={!safeMusicStatus.originUrl} 122 + class:cursor-default={!safeMusicStatus.originUrl} 123 + class:opacity-70={!safeMusicStatus.originUrl} 172 124 > 173 - <Radio class="h-3 w-3" /> 174 - {formatServiceName(safeMusicStatus.musicServiceBaseDomain)} via {safeMusicStatus.submissionClientAgent} 125 + {safeMusicStatus.trackName} 175 126 </a> 176 - {/if} 127 + 128 + <!-- Artists --> 129 + <p 130 + class="mt-1 flex max-w-full items-start gap-1.5 text-base wrap-break-word whitespace-normal text-ink-800 dark:text-ink-100" 131 + > 132 + <Users class="mt-0.5 h-4 w-4 flex-shrink-0 text-ink-600 dark:text-ink-300" /> 133 + {formatArtists(safeMusicStatus.artists)} 134 + </p> 135 + 136 + <!-- Album + Duration --> 137 + {#if safeMusicStatus.releaseName} 138 + <p 139 + class="mt-1 flex max-w-full items-start gap-1.5 text-sm wrap-break-word whitespace-normal text-ink-700 dark:text-ink-200" 140 + > 141 + <Album class="mt-0.5 h-4 w-4 flex-shrink-0 text-ink-500 dark:text-ink-400" /> 142 + <span> 143 + {safeMusicStatus.releaseName} 144 + 145 + {#if safeMusicStatus.duration} 146 + <span 147 + class="ml-1 inline-flex items-center gap-1 text-ink-600 dark:text-ink-300" 148 + > 149 + · <Clock class="h-3 w-3" /> 150 + {formatDuration(safeMusicStatus.duration)} 151 + </span> 152 + {/if} 153 + </span> 154 + </p> 155 + {/if} 156 + </div> 157 + 158 + <!-- Footer / Meta --> 159 + <div class="flex items-center gap-2 text-xs text-ink-700 dark:text-ink-200"> 160 + <time datetime={safeMusicStatus.playedTime}> 161 + {formatRelativeTime(safeMusicStatus.playedTime)} 162 + </time> 163 + 164 + {#if safeMusicStatus.musicServiceBaseDomain} 165 + <span class="text-ink-600 dark:text-ink-300">·</span> 166 + 167 + <a 168 + href="https://teal.fm" 169 + target="_blank" 170 + rel="noopener noreferrer" 171 + class="inline-flex items-center gap-1 transition-colors hover:text-primary-600 dark:hover:text-primary-400" 172 + title="Powered by teal.fm" 173 + > 174 + <Radio class="h-3 w-3" /> 175 + {formatServiceName(safeMusicStatus.musicServiceBaseDomain)} via {safeMusicStatus.submissionClientAgent} 176 + </a> 177 + {/if} 178 + </div> 177 179 </div> 178 180 </div> 179 181 </div>
+1 -1
src/lib/components/layout/main/card/ProfileCard.svelte
··· 109 109 110 110 {#if safeProfile.description} 111 111 <p 112 - class="mb-4 wrap-break-words break-all whitespace-pre-wrap text-ink-700 dark:text-ink-200" 112 + class="wrap-break-words mb-4 break-all whitespace-pre-wrap text-ink-700 dark:text-ink-200" 113 113 > 114 114 {safeProfile.description} 115 115 </p>