Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessibility refactor #418

Merged
merged 11 commits into from
Jul 18, 2024
49 changes: 43 additions & 6 deletions hushline/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ h2 {
h1+p:not(.instr):not(.message):not(.badge):not(.landing-message),
h2+p:not(.instr):not(.message):not(.badge):not(.landing-message) {
margin-top: .5rem;
margin-bottom: 1.5rem;
margin-bottom: 1rem;
}

h1+p+p,
Expand Down Expand Up @@ -689,6 +689,10 @@ img.upgrade {
border-bottom: var(--border);
}

.tab-list li {
display: flex;
}

.tab-list::-webkit-scrollbar {
display: none;
}
Expand All @@ -697,7 +701,7 @@ img.upgrade {
.tab {
min-height: 0px;
min-width: auto;
padding: .75rem .625rem;
padding: 1rem .625rem;
font-size: var(--font-size-base);
font-family: var(--font-sans);
white-space: nowrap;
Expand All @@ -721,7 +725,7 @@ img.upgrade {

/* Create an active/current tablink class */
.tab.active {
border-bottom: 3px solid var(--color-brand);
box-shadow: 0 -3px 0 inset var(--color-brand);
}

.tab.active {
Expand Down Expand Up @@ -777,6 +781,19 @@ img.upgrade {
/* Dropdown Button */
.dropbtn {
cursor: pointer;
background: none;
padding: 0;
min-height: 0px;
min-width: 0px;
box-shadow: none;
margin: 0;
color: var(--color-text);
font-family: var(--font-sans);
}

.dropbtn:focus-within {
outline: none;
border: 2px solid var(--color-text);
}

.dropdown a {
Expand Down Expand Up @@ -971,7 +988,7 @@ a.dropbtn:hover {
}

.user p.bio {
margin: .625rem 0 .0125rem 0;
margin: 0 0 .0125rem 0;
}

textarea#bio {
Expand Down Expand Up @@ -999,17 +1016,31 @@ p.helper {

.search-box {
position: relative;
display: inline-block;
display: flex;
align-items: center;
gap: 8px;
width: fit-content;
}

#searchIcon {
position: absolute;
left: 12px;
top: 47%;
margin: 0;
}

#searchIcon img {
width: 20px;
}

#searchInput {
width: 100%;
padding-left: 42px;
padding-right: 2.5rem;
box-sizing: border-box;
margin-bottom: 0;
margin-top: 1rem;
min-width: 320px;
margin-top: 1rem;
}

#clearIcon {
Expand All @@ -1019,6 +1050,12 @@ p.helper {
transform: translateY(-50%);
color: #666;
font-size: 24px;
min-width: 0px;
min-height: 0px;
margin: 0px;
box-shadow: none;
background: transparent;
padding: 0px 2px;
visibility: hidden; /* Hide the icon initially */
cursor: pointer;
}
Expand Down
Binary file added hushline/static/img/app/icon-search.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 56 additions & 13 deletions hushline/static/js/directory.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ document.addEventListener('DOMContentLoaded', function () {
function highlightMatch(text, query) {
if (!query) return text; // If no query, return the text unmodified
const regex = new RegExp(`(${query})`, 'gi'); // Case-insensitive matching
return text.replace(regex, '<span class="search-highlight">$1</span>');
return text.replace(regex, '<mark class="search-highlight">$1</mark>');
}


Expand Down Expand Up @@ -93,9 +93,11 @@ document.addEventListener('DOMContentLoaded', function () {
badgeContainer += '<p class="badge">⭐️ Verified</p>';
}

console.log(user, 'user')
const userDiv = document.createElement('div');
const userDiv = document.createElement('article');
userDiv.className = 'user';
const isVerified = user.is_verified ? 'Verified' : '';
const userType = user.is_admin ? `${isVerified} admin user` : `${isVerified} User`;
userDiv.setAttribute('aria-label', `${userType}, Display name:${user.display_name || user.primary_username}, Username: ${user.primary_username}, Bio: ${user.bio || 'No bio'}`);
userDiv.innerHTML = `
<h3>${displayNameHighlighted}</h3>
<p class="meta">@${usernameHighlighted}</p>
Expand All @@ -122,18 +124,59 @@ document.addEventListener('DOMContentLoaded', function () {
clearIcon.style.visibility = query.length ? 'visible' : 'hidden';
}

tabs.forEach(tab => {
tab.addEventListener('click', function () {
tabs.forEach(t => t.classList.remove('active'));
contents.forEach(c => c.classList.remove('active'));

tab.classList.add('active');
const activeContent = document.getElementById(tab.getAttribute('data-tab'));
activeContent.classList.add('active');
function activateTab(event) {
const selectedTab = event.target;
const targetPanel = document.getElementById(selectedTab.getAttribute('aria-controls'));

handleSearchInput(); // Filter again when tab changes
updatePlaceholder();
// Deselect all tabs and hide all panels
tabs.forEach(tab => {
tab.setAttribute('aria-selected', 'false');
tab.classList.remove('active');
document.getElementById(tab.getAttribute('aria-controls')).hidden = true;
});

// Select the clicked tab and show the corresponding panel
selectedTab.setAttribute('aria-selected', 'true');
selectedTab.classList.add('active');
targetPanel.hidden = false;

handleSearchInput(); // Filter again when tab changes
updatePlaceholder();

}

function handleKeydown(event) {
const { key } = event;
const currentTab = event.target;
let newTab;

switch (key) {
case 'ArrowLeft':
newTab = currentTab.parentElement.previousElementSibling?.querySelector('.tab');
break;
case 'ArrowRight':
newTab = currentTab.parentElement.nextElementSibling?.querySelector('.tab');
break;
case 'Home':
newTab = tabs[0];
break;
case 'End':
newTab = tabs[tabs.length - 1];
break;
default:
return;
}

if (newTab) {
newTab.focus();
newTab.click();
event.preventDefault();
}
}

tabs.forEach(tab => {
tab.addEventListener('click', activateTab);
tab.addEventListener('keydown', handleKeydown);
});

searchInput.addEventListener('input', handleSearchInput);
Expand Down
5 changes: 5 additions & 0 deletions hushline/static/js/nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ document.addEventListener('DOMContentLoaded', function() {
dropdownContent.classList.toggle('show');
dropdownContent.style.animation = dropdownContent.classList.contains('show') ? 'fadeInSlideDown 0.3s ease forwards' : 'fadeOutSlideUp 0.3s ease forwards';
dropdownIcon.classList.toggle('rotate-icon');
const expanded = this.getAttribute('aria-expanded') === 'true' || false;
this.setAttribute('aria-expanded', !expanded);
dropdownContent.hidden = expanded;
});

window.addEventListener('click', function(event) {
if (!dropdownToggle.contains(event.target) && dropdownContent.classList.contains('show')) {
dropdownContent.classList.remove('show');
dropdownIcon.classList.remove('rotate-icon');
this.setAttribute('aria-expanded', 'false');
dropdownContent.hidden = true;
}
});
}
Expand Down
60 changes: 49 additions & 11 deletions hushline/static/js/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,58 @@ document.addEventListener('DOMContentLoaded', function() {
const tabContents = document.querySelectorAll('.tab-content');
const bioCountEl = document.querySelector('.bio-count');

function removeActiveClasses() {
tabs.forEach(tab => tab.classList.remove('active'));
tabContents.forEach(content => content.style.display = 'none');
function activateTab(event) {
const selectedTab = event.target;
const targetPanel = document.getElementById(selectedTab.getAttribute('aria-controls'));

// Deselect all tabs and hide all panels
tabs.forEach(tab => {
tab.setAttribute('aria-selected', 'false');
tab.classList.remove('active');
const panel = document.getElementById(tab.getAttribute('aria-controls'));
panel.hidden = true;
panel.style.display = 'none';
});

// Select the clicked tab and show the corresponding panel
selectedTab.setAttribute('aria-selected', 'true');
selectedTab.classList.add('active');
targetPanel.hidden = false;
targetPanel.style.display = 'block';
}

function handleKeydown(event) {
const { key } = event;
const currentTab = event.target;
let newTab;

switch (key) {
case 'ArrowLeft':
newTab = currentTab.parentElement.previousElementSibling?.querySelector('.tab');
break;
case 'ArrowRight':
newTab = currentTab.parentElement.nextElementSibling?.querySelector('.tab');
break;
case 'Home':
newTab = tabs[0];
break;
case 'End':
newTab = tabs[tabs.length - 1];
break;
default:
return;
}

if (newTab) {
newTab.focus();
newTab.click();
event.preventDefault();
}
}

tabs.forEach(tab => {
tab.addEventListener('click', function() {
removeActiveClasses();
this.classList.add('active');
const activeTabContent = document.getElementById(this.getAttribute('data-tab'));
if (activeTabContent) {
activeTabContent.style.display = 'block';
}
});
tab.addEventListener('click', activateTab);
tab.addEventListener('keydown', handleKeydown);
});

if (tabs.length > 0) {
Expand Down
13 changes: 7 additions & 6 deletions hushline/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@

{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="flash-messages">
<div class="flash-messages" role="alert">
{% for message in messages %}
<div class="flash-message">{{ message }}</div>
<div class="flash-message">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
Expand All @@ -54,14 +54,15 @@ <h1>🤫 Hush Line</h1>
<li><a href="{{ url_for('inbox', username=session.username) }}">Inbox</a></li>
<li><a href="{{ url_for('submit_message', username=session.username) }}">Submit Message</a></li>
<li class="dropdown">
<a href="#" class="dropbtn">@{{ session['username'] }} <img class="dropdown-icon"
src="{{ url_for('static', filename='img/app/dropdown.png') }}" alt="Dropdown"></a>
<div class="dropdown-content">
<button type="button" aria-expanded="false" aria-controls="dropdown-content" class="dropbtn">
@{{ session['username'] }} <img class="dropdown-icon" src="/static/img/app/dropdown.png" alt="Dropdown">
</button>
<div id="dropdown-content" class="dropdown-content" hidden>
<ul>
<li><a href="{{ url_for('settings.index') }}">Settings</a></li>
<li><a href="{{ url_for('logout') }}">Logout</a></li>
</ul>
</div>
</div>
</li>
{% else %}
<li><a href="{{ url_for('login') }}">Login</a></li>
Expand Down
29 changes: 18 additions & 11 deletions hushline/templates/directory.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,27 @@ <h2>User Directory</h2>
<p class="helper">👋 Welcome to Hush Line! Find users who've opted into being listed below.</p>
{% endif %}
<div class="directory-tabs">
<ul class="tab-list">
<li>
<button class="tab active" data-tab="verified" type="button">Verified</button>
<ul class="tab-list" role="tablist">
<li role="presentation">
<button type="button" class="tab active" data-tab="verified" role="tab" aria-selected="true" aria-controls="verified" id="verified-tab" type="button">Verified</button>
</li>
<li>
<button class="tab" data-tab="all" type="button">All</button>
<li role="presentation">
<button type="button" class="tab" data-tab="all" role="tab" aria-selected="false" aria-controls="all" id="all-tab" type="button">All</button>
</li>
</ul>
</div>
<div class="search-box">
<label
id="searchIcon"
for="searchInput"
aria-label="Search users"
>
<img src="../static/img/app/icon-search.png" alt="">
</label>
<input type="text" id="searchInput" placeholder="Search users...">
<span id="clearIcon" style="cursor: pointer;">&times;</span>
<button id="clearIcon" type="button" style="cursor: pointer;" aria-label="Clear search field">&times;</button>
</div>
<div id="verified" class="tab-content active">
<div id="verified" class="tab-content active" role="tabpanel" aria-labelledby="verified-tab">
{% if not logged_in %}
<p class="meta dirMeta">⭐️ Verified accounts are offered to journalists, businesses, and other public figures and
have manually verified their identities with a member of our staff. <a
Expand All @@ -29,7 +36,7 @@ <h2>User Directory</h2>
<div class="user-list">
{% for user in users %}
{% if user.is_verified and user.show_in_directory %}
<div class="user">
<article class="user">
<h3>{{ user.display_name or user.primary_username }}</h3>
{% if user.primary_username %}
<p class="meta">@{{ user.primary_username }}</p>
Expand All @@ -50,7 +57,7 @@ <h3>{{ user.display_name or user.primary_username }}</h3>
data-bio="{{ user.bio }}">Report Account</a>
{% endif %}
</div>
</div>
</article>
{% endif %}
{% else %}
<p class="empty-message"><span class="emoji-message">🙈</span><br>Nothing to see here...</p>
Expand All @@ -61,7 +68,7 @@ <h3>{{ user.display_name or user.primary_username }}</h3>
<div class="user-list">
{% for user in users %}
{% if user.show_in_directory %}
<div class="user">
<article class="user">
<h3>{{ user.display_name or user.primary_username }}</h3>
{% if user.primary_username %}
<p class="meta">@{{ user.primary_username }}</p>
Expand All @@ -87,7 +94,7 @@ <h3>{{ user.display_name or user.primary_username }}</h3>
data-bio="{{ user.bio }}">Report Account</a>
{% endif %}
</div>
</div>
</article>
{% endif %}
{% else %}
<p class="empty-message"><span class="emoji-message">🙈</span><br>Nothing to see here...</p>
Expand Down
Loading
Loading