From e981782dd77624501242c815f49c26ebea77f2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Fischer?= Date: Sun, 5 Jul 2020 05:42:47 +0200 Subject: [PATCH] Accessibility enhancements: catching a11y errors with WAVE (#180) * Added alt text for all images * Added aria labels for most buttons that needed them. Added html language label * Added remaining button labels * Added remaining missing labels. Tagged where labels made no sense as "aria-hidden=true" * Fixed some of the low color contrast issues for Light theme * Replaced broken ARIA link with a new ARIA label * Fixed skipped heading levels * Fixed missing fieldset and one orphaned label (combined the two) * Changed the other orphaned label to a fieldset legend * Removed color changes * Added dynamic ARIA label for main Play/Pause button. Removed the two FIXMEs in index.html * Added default content to table header * Changed input type from text to hidden for hidden inputs * Added proper alt text for cover art * Final version of the template row * Added semantic markup to Page Regions * Missing alt text + truncated alt text - Added missing alt text for cover images in library and floating player. - Added JS to truncate alt text for cover images that was too long. * Sanitize the code Co-authored-by: Terry Geng --- templates/index.html | 2 +- web/js/main.mjs | 216 +++++++++++++++++--------------- web/js/util.js | 17 +++ web/sass/app-dark.scss | 2 +- web/sass/app.scss | 2 +- web/sass/main.scss | 201 +++++++++++++++++++----------- web/templates/index.html | 262 +++++++++++++++++++++++---------------- 7 files changed, 412 insertions(+), 290 deletions(-) diff --git a/templates/index.html b/templates/index.html index 4f5f1c7..c22c3b0 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1 +1 @@ -botamusique web interface

Music Library

Filters

{% for tag in tags_color_lookup.keys() %} {{ tag }} {% endfor %}
{% if upload_enabled %}
{% else %}
Add URL
Add Radio
\ No newline at end of file +botamusique web interface
#TitleUrl/PathAction
A loading spinner
A drawing of an empty box.
See item on the playlist.

Music Library

Filters


Type
Tags{% for tag in tags_color_lookup.keys() %} {{ tag }} {% endfor %}
A loading spinner
{% if upload_enabled %}
{% else %}

Add URL

Add Radio

\ No newline at end of file diff --git a/web/js/main.mjs b/web/js/main.mjs index 3533473..ce1300f 100644 --- a/web/js/main.mjs +++ b/web/js/main.mjs @@ -10,9 +10,10 @@ import { isOverflown, setProgressBar, secondsToStr, + coverArtString, } from './util'; -$('#uploadSelectFile').on('change', function() { +$('#uploadSelectFile').on('change', function () { // get the file name const fileName = $(this).val().replace('C:\\fakepath\\', ' '); // replace the "Choose a file" label @@ -83,7 +84,7 @@ fastForwardBtn.on('click', () => { }); document.getElementById('clear-playlist-btn').addEventListener('click', () => { - request('post', {action: 'clear'}); + request('post', { action: 'clear' }); }); // eslint-disable-next-line guard-for-in @@ -100,14 +101,14 @@ function request(_url, _data, refresh = false) { url: _url, data: _data, statusCode: { - 200: function(data) { + 200: function (data) { if (data.ver !== playlist_ver) { checkForPlaylistUpdate(); } updateControls(data.empty, data.play, data.mode, data.volume); updatePlayerPlayhead(data.playhead); }, - 403: function() { + 403: function () { location.reload(true); }, }, @@ -123,6 +124,7 @@ function addPlaylistItem(item) { pl_title_element.html(item.title); pl_artist_element.html(item.artist); pl_thumb_element.attr('src', item.thumbnail); + pl_thumb_element.attr('alt', coverArtString(item.title)); pl_type_element.html(item.type); pl_path_element.html(item.path); @@ -134,13 +136,13 @@ function addPlaylistItem(item) { tags.empty(); const tag_edit_copy = pl_tag_edit_element.clone(); - tag_edit_copy.click(function() { + tag_edit_copy.click(function () { addTagModalShow(item.id, item.title, item.tags); }); tag_edit_copy.appendTo(tags); if (item.tags.length > 0) { - item.tags.forEach(function(tag_tuple) { + item.tags.forEach(function (tag_tuple) { const tag_copy = tag_element.clone(); tag_copy.html(tag_tuple[0]); tag_copy.addClass('badge-' + tag_tuple[1]); @@ -157,7 +159,7 @@ function addPlaylistItem(item) { function displayPlaylist(data) { playlist_table.animate({ opacity: 0, - }, 200, function() { + }, 200, function () { playlist_loading.hide(); $('.playlist-item').remove(); const items = data.items; @@ -180,9 +182,9 @@ function displayPlaylist(data) { } items.forEach( - function(item) { - addPlaylistItem(item); - }, + function (item) { + addPlaylistItem(item); + }, ); if (items.length < length && start_from + items.length < length) { @@ -221,7 +223,7 @@ function insertExpandPrompt(real_from, real_to, display_from, display_to, total_ expand_copy.addClass('playlist-item'); expand_copy.appendTo(playlist_table); - expand_copy.click(function() { + expand_copy.click(function () { playlist_range_from = real_from; playlist_range_to = real_to; updatePlaylist(); @@ -231,7 +233,7 @@ function insertExpandPrompt(real_from, real_to, display_from, display_to, total_ function updatePlaylist() { playlist_table.animate({ opacity: 0, - }, 200, function() { + }, 200, function () { playlist_empty.addClass('d-none'); playlist_loading.show(); playlist_table.find('.playlist-item').css('opacity', 0); @@ -248,7 +250,7 @@ function updatePlaylist() { data: data, statusCode: { 200: displayPlaylist, - 204: function() { + 204: function () { playlist_loading.hide(); playlist_empty.removeClass('d-none'); $('.playlist-item').remove(); @@ -266,7 +268,7 @@ function checkForPlaylistUpdate() { type: 'POST', url: 'post', statusCode: { - 200: function(data) { + 200: function (data) { if (data.ver !== playlist_ver) { playlist_ver = data.ver; playlist_range_from = 0; @@ -297,18 +299,18 @@ function checkForPlaylistUpdate() { function bindPlaylistEvent() { $('.playlist-item-play').unbind().click( - function(e) { - request('post', { - 'play_music': ($(e.currentTarget).parent().parent().parent().find('.playlist-item-index').html() - 1), - }); - }, + function (e) { + request('post', { + 'play_music': ($(e.currentTarget).parent().parent().parent().find('.playlist-item-index').html() - 1), + }); + }, ); $('.playlist-item-trash').unbind().click( - function(e) { - request('post', { - 'delete_music': ($(e.currentTarget).parent().parent().parent().find('.playlist-item-index').html() - 1), - }); - }, + function (e) { + request('post', { + 'delete_music': ($(e.currentTarget).parent().parent().parent().find('.playlist-item-index').html() - 1), + }); + }, ); } @@ -323,9 +325,15 @@ function updateControls(empty, play, mode, volume) { if (play) { playing = true; playPauseBtn.find('[data-fa-i2svg]').removeClass('fa-play').addClass('fa-pause'); + // PR #180: Since this button changes behavior dynamically, we change its + // ARIA labels in JS instead of only adding them statically in the HTML + playPauseBtn.attr('aria-label', 'Pause'); } else { playing = false; playPauseBtn.find('[data-fa-i2svg]').removeClass('fa-pause').addClass('fa-play'); + // PR #180: Since this button changes behavior dynamically, we change its + // ARIA labels in JS instead of only adding them statically in the HTML + playPauseBtn.attr('aria-label', 'Play'); } } @@ -409,7 +417,7 @@ function setFilterType(event, type) { } // Bind Event -$('.filter-tag').click(function(e) { +$('.filter-tag').click(function (e) { const tag = $(e.currentTarget); if (!tag.hasClass('tag-clicked')) { tag.addClass('tag-clicked'); @@ -421,10 +429,10 @@ $('.filter-tag').click(function(e) { updateResults(); }); -filter_dir.change(function() { +filter_dir.change(function () { updateResults(); }); -filter_keywords.change(function() { +filter_keywords.change(function () { updateResults(); }); @@ -432,64 +440,64 @@ const item_template = $('#library-item'); function bindLibraryResultEvent() { $('.library-thumb-col').unbind().hover( - function(e) { - $(e.currentTarget).find('.library-thumb-grp').addClass('library-thumb-grp-hover'); - }, - function(e) { - $(e.currentTarget).find('.library-thumb-grp').removeClass('library-thumb-grp-hover'); - }, + function (e) { + $(e.currentTarget).find('.library-thumb-grp').addClass('library-thumb-grp-hover'); + }, + function (e) { + $(e.currentTarget).find('.library-thumb-grp').removeClass('library-thumb-grp-hover'); + }, ); $('.library-info-title').unbind().hover( - function(e) { - $(e.currentTarget).parent().find('.library-thumb-grp').addClass('library-thumb-grp-hover'); - }, - function(e) { - $(e.currentTarget).parent().find('.library-thumb-grp').removeClass('library-thumb-grp-hover'); - }, + function (e) { + $(e.currentTarget).parent().find('.library-thumb-grp').addClass('library-thumb-grp-hover'); + }, + function (e) { + $(e.currentTarget).parent().find('.library-thumb-grp').removeClass('library-thumb-grp-hover'); + }, ); $('.library-item-play').unbind().click( - function(e) { - request('post', { - 'add_item_at_once': $(e.currentTarget).parent().parent().parent().find('.library-item-id').val(), - }); - }, + function (e) { + request('post', { + 'add_item_at_once': $(e.currentTarget).parent().parent().parent().find('.library-item-id').val(), + }); + }, ); $('.library-item-trash').unbind().click( - function(e) { - request('post', { - 'delete_item_from_library': $(e.currentTarget).parent().parent().find('.library-item-id').val(), - }); - updateResults(active_page); - }, + function (e) { + request('post', { + 'delete_item_from_library': $(e.currentTarget).parent().parent().find('.library-item-id').val(), + }); + updateResults(active_page); + }, ); $('.library-item-download').unbind().click( - function(e) { - const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); - // window.open('/download?id=' + id); - downloadId(id); - }, + function (e) { + const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); + // window.open('/download?id=' + id); + downloadId(id); + }, ); $('.library-item-add-next').unbind().click( - function(e) { - const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); - request('post', { - 'add_item_next': id, - }); - }, + function (e) { + const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); + request('post', { + 'add_item_next': id, + }); + }, ); $('.library-item-add-bottom').unbind().click( - function(e) { - const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); - request('post', { - 'add_item_bottom': id, - }); - }, + function (e) { + const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); + request('post', { + 'add_item_bottom': id, + }); + }, ); } @@ -510,6 +518,7 @@ function addResultItem(item) { title_element.html(item.title); artist_element.html(item.artist ? ('- ' + item.artist) : ''); thumb_element.attr('src', item.thumb); + thumb_element.attr('alt', coverArtString(item.title)); type_element.html('[' + item.type + ']'); path_element.html(item.path); @@ -520,13 +529,13 @@ function addResultItem(item) { tags.empty(); const tag_edit_copy = tag_edit_element.clone(); - tag_edit_copy.click(function() { + tag_edit_copy.click(function () { addTagModalShow(item.id, item.title, item.tags); }); tag_edit_copy.appendTo(tags); if (item.tags.length > 0) { - item.tags.forEach(function(tag_tuple) { + item.tags.forEach(function (tag_tuple) { const tag_copy = tag_element.clone(); tag_copy.html(tag_tuple[0]); tag_copy.addClass('badge-' + tag_tuple[1]); @@ -544,7 +553,7 @@ function addResultItem(item) { function getFilters(dest_page = 1) { const tags = $('.tag-clicked'); const tags_list = []; - tags.each(function(index, tag) { + tags.each(function (index, tag) { tags_list.push(tag.innerHTML); }); @@ -575,19 +584,19 @@ function updateResults(dest_page = 1) { lib_group.animate({ opacity: 0, - }, 200, function() { + }, 200, function () { $.ajax({ type: 'POST', url: 'library', data: data, statusCode: { 200: processResults, - 204: function() { + 204: function () { lib_loading.hide(); lib_empty.show(); page_ul.empty(); }, - 403: function() { + 403: function () { location.reload(true); }, }, @@ -651,7 +660,7 @@ document.getElementById('library-download-btn').addEventListener('click', () => }); document.getElementById('library-rescan-btn').addEventListener('click', () => { - request('post', {action: 'rescan'}); + request('post', { action: 'rescan' }); updateResults(); }); @@ -671,16 +680,16 @@ const page_no = $('.library-page-no'); function processResults(data) { lib_group.animate({ opacity: 0, - }, 200, function() { + }, 200, function () { lib_loading.hide(); const total_pages = data.total_pages; const active_page = data.active_page; const items = data.items; items.forEach( - function(item) { - addResultItem(item); - bindLibraryResultEvent(); - }, + function (item) { + addResultItem(item); + bindLibraryResultEvent(); + }, ); page_ul.empty(); @@ -698,7 +707,7 @@ function processResults(data) { page_no_copy = page_no.clone(); page_no_copy.html('«'); - page_no_copy.click(function(e) { + page_no_copy.click(function (e) { updateResults(1); }); @@ -714,7 +723,7 @@ function processResults(data) { if (active_page === i) { page_li_copy.addClass('active'); } else { - page_no_copy.click(function(e) { + page_no_copy.click(function (e) { const _page_no = $(e.currentTarget).html(); updateResults(_page_no); }); @@ -728,7 +737,7 @@ function processResults(data) { page_no_copy = page_no.clone(); page_no_copy.html('»'); - page_no_copy.click(function(e) { + page_no_copy.click(function (e) { updateResults(total_pages); }); @@ -757,11 +766,11 @@ function addTagModalShow(_id, _title, _tag_tuples) { add_tag_modal_title.html('Edit tags for ' + _title); add_tag_modal_item_id.val(_id); add_tag_modal_tags.empty(); - _tag_tuples.forEach(function(tag_tuple) { + _tag_tuples.forEach(function (tag_tuple) { modal_tag_text.html(tag_tuple[0]); const tag_copy = modal_tag.clone(); const modal_tag_remove = tag_copy.find('.modal-tag-remove'); - modal_tag_remove.click(function(e) { + modal_tag_remove.click(function (e) { $(e.currentTarget).parent().remove(); }); tag_copy.show(); @@ -772,14 +781,14 @@ function addTagModalShow(_id, _title, _tag_tuples) { } document.getElementById('addTagModalAddBtn').addEventListener('click', () => { - const new_tags = add_tag_modal_input.val().split(',').map(function(str) { + const new_tags = add_tag_modal_input.val().split(',').map(function (str) { return str.trim(); }); - new_tags.forEach(function(tag) { + new_tags.forEach(function (tag) { modal_tag_text.html(tag); const tag_copy = modal_tag.clone(); const modal_tag_remove = tag_copy.find('.modal-tag-remove'); - modal_tag_remove.click(function(e) { + modal_tag_remove.click(function (e) { $(e.currentTarget).parent().remove(); }); tag_copy.show(); @@ -792,7 +801,7 @@ document.getElementById('addTagModalAddBtn').addEventListener('click', () => { document.getElementById('addTagModalSubmit').addEventListener('click', () => { const all_tags = $('.modal-tag-text'); const tags = []; - all_tags.each(function(i, element) { + all_tags.each(function (i, element) { if (element.innerHTML) { tags.push(element.innerHTML); } @@ -820,7 +829,7 @@ let volume_popover_instance = null; let volume_popover_show = false; let volume_update_timer; -volumePopoverBtn.addEventListener('click', function(e) { +volumePopoverBtn.addEventListener('click', function (e) { e.stopPropagation(); if (!volume_popover_show) { @@ -842,7 +851,7 @@ volumePopoverBtn.addEventListener('click', function(e) { } volume_popover_show = !volume_popover_show; - document.addEventListener('click', function() { + document.addEventListener('click', function () { volumePopoverDiv.removeAttribute('data-show'); if (volume_popover_instance) { volume_popover_instance.destroy(); @@ -854,7 +863,7 @@ volumePopoverBtn.addEventListener('click', function(e) { }); }); -volumePopoverBtn.addEventListener('click', function(e) { +volumePopoverBtn.addEventListener('click', function (e) { e.stopPropagation(); }); @@ -870,11 +879,11 @@ volumeSlider.addEventListener('change', (e) => { }); document.getElementById('volume-down-btn').addEventListener('click', () => { - request('post', {action: 'volume_down'}); + request('post', { action: 'volume_down' }); }); document.getElementById('volume-up-btn').addEventListener('click', () => { - request('post', {action: 'volume_up'}); + request('post', { action: 'volume_up' }); }); // --------------------- @@ -970,7 +979,7 @@ function uploadNextFile() { const file = filesToProceed.shift(); const file_progress_item = filesProgressItem[file.name]; - req.addEventListener('load', function() { + req.addEventListener('load', function () { if (this.status === 200) { setProgressBar(file_progress_item.progress, 1); file_progress_item.progress.classList.add('bg-success'); @@ -1003,7 +1012,7 @@ function uploadNextFile() { } }); - req.upload.addEventListener('progress', function(e) { + req.upload.addEventListener('progress', function (e) { if (e.lengthComputable) { const percent = e.loaded / e.total; setProgressBar(file_progress_item.progress, percent, Math.floor(percent * 100) + '%'); @@ -1050,12 +1059,12 @@ const musicUrlInput = document.getElementById('music-url-input'); const radioUrlInput = document.getElementById('radio-url-input'); document.getElementById('add-music-url').querySelector('button').addEventListener('click', () => { - request('post', {add_url: musicUrlInput.value}); + request('post', { add_url: musicUrlInput.value }); musicUrlInput.value = ''; }); document.getElementById('add-radio-url').querySelector('button').addEventListener('click', () => { - request('post', {add_radio: radioUrlInput.value}); + request('post', { add_radio: radioUrlInput.value }); radioUrlInput.value = ''; }); @@ -1077,15 +1086,15 @@ const playerSkipBtn = document.getElementById('playerSkipBtn'); let currentPlayingItem = null; playerPlayBtn.addEventListener('click', () => { - request('post', {action: 'resume'}); + request('post', { action: 'resume' }); }); playerPauseBtn.addEventListener('click', () => { - request('post', {action: 'pause'}); + request('post', { action: 'pause' }); }); playerSkipBtn.addEventListener('click', () => { - request('post', {action: 'next'}); + request('post', { action: 'next' }); }); document.getElementById('player-toast').addEventListener('click', () => { @@ -1111,6 +1120,7 @@ function updatePlayerInfo(item) { playerTitle.textContent = item.title; playerArtist.textContent = item.artist; playerArtwork.setAttribute('src', item.thumbnail); + playerArtwork.setAttribute('alt', coverArtString(item.title)); if (isOverflown(playerTitle)) { playerTitle.classList.add('scrolling'); @@ -1159,7 +1169,7 @@ function updatePlayerPlayhead(playhead) { player_playhead_position = playhead; setProgressBar(playerBar, player_playhead_position / currentPlayingItem.duration, secondsToStr(player_playhead_position)); if (playing) { - playhead_timer = setInterval(function() { + playhead_timer = setInterval(function () { player_playhead_position += 0.3; setProgressBar(playerBar, player_playhead_position / currentPlayingItem.duration, secondsToStr(player_playhead_position)); }, 300); // delay in milliseconds @@ -1174,7 +1184,7 @@ function updatePlayerPlayhead(playhead) { } } -playerBarBox.addEventListener('mousedown', function() { +playerBarBox.addEventListener('mousedown', function () { if (currentPlayingItem && currentPlayingItem.duration > 0) { playerBarBox.addEventListener('mousemove', playheadDragged); clearInterval(playhead_timer); @@ -1182,7 +1192,7 @@ playerBarBox.addEventListener('mousedown', function() { } }); -playerBarBox.addEventListener('mouseup', function(event) { +playerBarBox.addEventListener('mouseup', function (event) { playerBarBox.removeEventListener('mousemove', playheadDragged); const percent = (event.clientX - playerBarBox.getBoundingClientRect().x) / playerBarBox.clientWidth; request('post', { diff --git a/web/js/util.js b/web/js/util.js index e25813f..2c50c49 100644 --- a/web/js/util.js +++ b/web/js/util.js @@ -16,3 +16,20 @@ export function secondsToStr(seconds) { const secs = seconds % 60; return ('00' + mins).slice(-2) + ':' + ('00' + secs).slice(-2); } + +export function coverArtString(title) { + + let nameOfSong = ""; + // The maximum length before we start truncating + const maxLength = 50; + + if (title.length > maxLength) { + // Name = longTitleTooLongToBeAGoodAltTex... + nameOfSong = title.substr(0, maxLength) + "\u2026"; + } else { + // Name = shortTitle + nameOfSong = title; + } + + return 'Cover art for ' + nameOfSong; +} \ No newline at end of file diff --git a/web/sass/app-dark.scss b/web/sass/app-dark.scss index c696fbe..4a9a7b2 100644 --- a/web/sass/app-dark.scss +++ b/web/sass/app-dark.scss @@ -2,4 +2,4 @@ @import '~bootstrap/scss/bootstrap'; @import '~bootswatch/dist/darkly/bootswatch'; -@import './main'; \ No newline at end of file +@import './main'; diff --git a/web/sass/app.scss b/web/sass/app.scss index 835e65c..383bbdf 100644 --- a/web/sass/app.scss +++ b/web/sass/app.scss @@ -1,3 +1,3 @@ @import '~bootstrap/scss/bootstrap'; -@import './main'; \ No newline at end of file +@import './main'; diff --git a/web/sass/main.scss b/web/sass/main.scss index 189ae27..638cb07 100644 --- a/web/sass/main.scss +++ b/web/sass/main.scss @@ -1,117 +1,141 @@ -.btn-space {margin-right: 5px;} +.btn-space { + margin-right: 5px; +} /* Playlist */ -.playlist-item {transition: all 0.2s ease-in-out;} -.playlist-artwork { - float: left; - margin-left: 10px; - white-space: nowrap; - overflow: hidden; +.playlist-item { + transition: all 0.2s ease-in-out; } + +.playlist-artwork { + float: left; + margin-left: 10px; + white-space: nowrap; + overflow: hidden; +} + .tag-space { margin-right: 3px; } + .tag-click { - cursor: pointer; - transition: 400ms; + cursor: pointer; + transition: 400ms; } + .tag-unclicked { - opacity: 0.6; + opacity: 0.6; } + .tag-clicked { - box-shadow: 2px 4px 10px #777777; - transform: scale(1.2); - opacity: 1; - margin: 5px; + box-shadow: 2px 4px 10px #777777; + transform: scale(1.2); + opacity: 1; + margin: 5px; } + .library-item { - display: flex; - padding: .5rem .5rem .5rem 0; - height: 72px; - transition: ease-in-out 200ms; + display: flex; + padding: .5rem .5rem .5rem 0; + height: 72px; + transition: ease-in-out 200ms; } + .library-thumb-img { - width: 70px; - height: 70px; - border-radius: 5px; + width: 70px; + height: 70px; + border-radius: 5px; } + .library-thumb-col { - position: relative; - padding-left: 0; - overflow: hidden; - margin: -0.5rem 1rem -0.5rem 0; + position: relative; + padding-left: 0; + overflow: hidden; + margin: -0.5rem 1rem -0.5rem 0; } + .library-thumb-grp { - position: absolute; - top: 0; - left: -95px; - width: 70px; - margin-left: 15px; - transition: left 300ms; - border-radius: 5px; - opacity: 0.7; - font-weight: 300; + position: absolute; + top: 0; + left: -95px; + width: 70px; + margin-left: 15px; + transition: left 300ms; + border-radius: 5px; + opacity: 0.7; + font-weight: 300; } + .library-thumb-grp-hover { - left: -15px; + left: -15px; } + .library-thumb-btn-up { - position: absolute !important; - top: 0; - height: 70px; - font-size: 2em; - padding-top: 10px; + position: absolute !important; + top: 0; + height: 70px; + font-size: 2em; + padding-top: 10px; } + .library-btn-svg { - width: 1rem; - fill: currentColor; + width: 1rem; + fill: currentColor; } + .library-info-col { - margin-right: 1rem; - padding: 3px 0; - display: flex; - flex-direction: column; - justify-content: center; - white-space: nowrap; - overflow: hidden; + margin-right: 1rem; + padding: 3px 0; + display: flex; + flex-direction: column; + justify-content: center; + white-space: nowrap; + overflow: hidden; } + .library-info-col .small { font-weight: 300; } + .library-action { margin-left: auto; } + .library-info-col .path { - font-style: italic !important; - font-weight: 300; + font-style: italic !important; + font-weight: 300; } /* Theme changer and player button */ .floating-button { - width: 50px; - height: 50px; - background-color: #aaaaaa40; - border-radius: 50%; - box-shadow: 0 6px 10px 0 #66666647; - transition: all 0.1s ease-in-out; - font-size: 25px; - color: #9896967a; - text-align: center; - line-height: 52px; - position: fixed; - right: 50px; + width: 50px; + height: 50px; + background-color: #aaaaaa40; + border-radius: 50%; + box-shadow: 0 6px 10px 0 #66666647; + transition: all 0.1s ease-in-out; + font-size: 25px; + color: #9896967a; + text-align: center; + line-height: 52px; + position: fixed; + right: 50px; } + .floating-button:hover { background-color: hsl(0, 0%, 43%); color: white; } + #volume-slider { margin-top: 4px; margin-right: 5px; } + .dropdown { display: inline-block; } + #volume-popover { position: relative; background: #333; @@ -122,45 +146,53 @@ border-radius: 4px; display: none; } + #volume-popover[data-show] { display: flex; } + #volume-popover a { cursor: pointer; } + #volume-popover-arrow, #volume-popover-arrow::before { - position: absolute; - width: 10px; - height: 10px; - z-index: -1; - top: 16px; - left: 46px; + position: absolute; + width: 10px; + height: 10px; + z-index: -1; + top: 16px; + left: 46px; } #volume-popover-arrow::before { - content: ''; - transform: rotate(45deg); - background: #333; + content: ''; + transform: rotate(45deg); + background: #333; } -#volume-popover[data-popper-placement^='top'] > #volume-popover-arrow { - bottom: -4px; + +#volume-popover[data-popper-placement^='top']>#volume-popover-arrow { + bottom: -4px; } + #playerToast { position: fixed; right: 20px; top: 20px; max-width: 800px; } + #playerContainer { display: flex; height: 105px; } + #playerArtwork { width: 80px; height: 80px; border-radius: 5px; } + #playerArtworkIdle { width: 80px; height: 80px; @@ -168,6 +200,7 @@ margin: auto; padding: 15px; } + #playerInfo { position: relative; padding-top: 6px; @@ -175,40 +208,58 @@ height: 80px; font-size: 15px; } + #playerTitle { display: block; white-space: nowrap; } + #playerArtist { display: block; white-space: nowrap; min-height: 20px; } + #playerActionBox { margin-top: 5px; display: flex; float: right; } + #playerBarBox { margin-top: 5px; height: 15px; width: 400px; cursor: pointer; } + .scrolling { animation: scrolling 8s linear infinite; } + @keyframes scrolling { 0% { transform: translateX(100%); opacity: 1; } - 95%{ + + 95% { transform: translateX(-90%); opacity: 1; } + 100% { transform: translateX(-100%); opacity: 0; } } + +// Allows us to have H3 with the size of an H5 +h3 { + font-size: 1.25rem; +} + +// Makes legend match the size of other labels +legend { + font-size: 1rem; +} diff --git a/web/templates/index.html b/web/templates/index.html index cbfe315..f87f011 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -1,5 +1,7 @@ + + @@ -12,28 +14,31 @@ - + -
+

Music Library

-
Filters
+

Filters


- -
+
+ Type
-
+
@@ -178,34 +191,39 @@
- +
- -
+
+ Tags {% for tag in tags_color_lookup.keys() %} - {{ tag }} + {{ tag }} {% endfor %} -
+
- + A loading spinner