fix: improve universal navbar UI (#51108)

This commit is contained in:
Victor Duarte
2023-09-19 07:50:20 -04:00
committed by GitHub
parent a41065875c
commit 869fb4fc9f
14 changed files with 252 additions and 293 deletions
+1 -3
View File
@@ -6,10 +6,8 @@ function MicrosoftLogo(
return (
<svg
version='1.1'
viewBox='0 0 610 130'
xmlns='http://www.w3.org/2000/svg'
height='800'
width='1200'
viewBox='-90.585 -32.25 785.07 193.5'
{...props}
>
<path
@@ -1,37 +1,18 @@
.exam-nav {
display: flex;
justify-content: space-around;
height: 80px;
background: var(--theme-color);
position: fixed;
z-index: 1000;
width: 100%;
top: 0;
}
.exam-nav .logo {
display: flex;
width: 50%;
align-items: center;
justify-content: center;
height: 80px;
background-color: var(--theme-color);
display: flex;
height: var(--header-height);
justify-content: space-between;
padding: 0 5px;
}
.exam-nav .freecodecamp-logo svg {
height: 80%;
width: 80%;
max-width: 360px;
}
.exam-nav .partner-logo svg {
height: 80%;
width: 80%;
max-width: 450px;
}
@media (max-width: 600px) {
.exam-nav .freecodecamp-logo svg {
height: 90%;
width: 90%;
@media (min-width: 401px) {
.exam-nav {
padding: 0 15px;
}
}
.exam-nav svg {
height: 32px;
}
@@ -10,12 +10,8 @@ const ExamNav = (): JSX.Element => {
return (
<nav aria-label={t('aria.primary-nav')} className='exam-nav' id='exam-nav'>
<div className='logo freecodecamp-logo'>
<FreeCodeCampLogo aria-hidden='true' />
</div>
<div className='logo partner-logo'>
<MicrosoftLogo aria-hidden='true' />
</div>
<FreeCodeCampLogo aria-hidden='true' />
<MicrosoftLogo aria-hidden='true' />
</nav>
);
};
@@ -1,81 +1,100 @@
.universal-nav {
align-items: center;
background-color: var(--theme-color);
color: var(--gray-00);
display: flex;
justify-content: space-between;
align-items: flex-start;
font-size: 18px;
height: var(--header-height);
background: var(--theme-color);
position: fixed;
z-index: 1000;
color: var(--gray-00);
width: 100%;
padding: 0 15px;
top: 0;
--search-box-form: 38px;
--lang-menu-height: 22.5rem;
justify-content: space-between;
padding: 0 5px;
}
@media (min-width: 401px) {
.universal-nav {
padding: 0 15px;
}
}
.universal-nav a {
text-decoration: none;
}
/**
* Wrapper for content to the left of the site logo
*/
.universal-nav-left {
display: flex;
flex: 1 0 33%;
margin-inline-start: 0;
z-index: 2000;
}
#universal-nav-logo {
display: flex;
/**
* Site header logo
*/
.universal-nav-logo {
align-items: center;
overflow: hidden;
display: flex;
margin-inline-end: 4px;
max-inline-size: max-content;
-webkit-overflow-scrolling: touch;
overflow: hidden;
-ms-overflow-scrolling: touch;
padding-top: 0.2em;
-webkit-overflow-scrolling: touch;
}
#universal-nav-logo:focus {
outline-offset: -3px;
@media (min-width: 980px) {
.universal-nav-logo {
margin-inline: 1em;
}
}
.universal-nav-right {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10px;
flex: 1 0 auto;
height: var(--header-height);
}
#universal-nav-logo:hover,
#universal-nav-logo:focus {
/* Overwrite global anchor hover and focus */
.universal-nav-logo:hover,
.universal-nav-logo:focus {
background-color: inherit;
}
#universal-nav-logo svg {
margin: 4px;
}
.nav-list {
display: none;
position: absolute;
background-color: var(--theme-color);
right: 0;
flex-wrap: wrap;
/**
* Wrapper for content to the right of the site logo
*/
.universal-nav-right {
display: flex;
justify-content: flex-end;
width: 100vw;
height: auto;
padding: 0;
list-style: none;
max-width: 15rem;
gap: 10px;
flex: 1 0 33%;
}
.lang-button-nav[aria-expanded='true'] + .nav-list,
.lang-button-nav[aria-expanded='false'].force-show + .nav-list {
/**
* Site header menu list
*/
.nav-list {
background-color: var(--theme-color);
display: none;
justify-content: flex-end;
list-style: none;
padding: 0;
position: absolute;
right: 0;
width: 100%;
}
@media (min-width: 980px) {
.nav-list {
max-width: 15rem;
}
}
/**
* Site header language list
*/
.lang-button-nav[aria-expanded='true'] + .nav-list {
-ms-overflow-style: none;
display: block;
top: 100%;
max-height: calc(100vh - var(--header-height));
overflow-y: auto;
scrollbar-width: none;
top: calc(var(--header-height));
}
.lang-button-nav[aria-expanded='true'] + .nav-list::-webkit-scrollbar {
display: none;
}
.exposed-button-nav:is(
@@ -103,28 +122,24 @@
fill: var(--gray-00);
}
.nav-list li {
width: 100%;
height: 2.5rem;
}
/**
* Site header sub menu items
*/
.nav-link {
margin: 0;
padding: 2px 15px 0;
display: flex;
align-items: center;
color: var(--gray-00);
background-color: var(--gray-90);
opacity: 1;
white-space: normal;
min-height: var(--header-height);
min-height: var(--header-sub-element-size);
width: 100%;
border: none;
height: 100%;
}
.nav-link:focus {
outline-offset: -4px;
outline-offset: -3px;
}
.nav-link:focus:not(:focus-visible) {
@@ -157,19 +172,10 @@ li > button.nav-link-signout:not([aria-disabled='true']):is(:hover, :focus) {
color: var(--gray-45);
}
.nav-link .fa-check,
.nav-link .fa-check-square {
width: 18px !important;
height: auto !important;
}
.nav-link-header label {
font-weight: normal;
margin-bottom: 0;
}
/**
* Check mark for current language
*/
.nav-lang-list-option[aria-current='true'] {
/* check mark for current language */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='32' height='32' preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16'%3E%3Cg fill='white'%3E%3Cpath d='M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06a.733.733 0 0 1 1.047 0l3.052 3.093l5.4-6.425a.247.247 0 0 1 .02-.022z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
background-size: 1.2rem;
background-position: calc(100% - 10px) center;
@@ -178,49 +184,54 @@ li > button.nav-link-signout:not([aria-disabled='true']):is(:hover, :focus) {
.nav-lang-list-option[aria-current='true']:hover,
.nav-lang-list-option[aria-current='true']:focus-visible {
/* check mark for current language */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='32' height='32' preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16'%3E%3Cg fill='currentColor'%3E%3Cpath d='M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06a.733.733 0 0 1 1.047 0l3.052 3.093l5.4-6.425a.247.247 0 0 1 .02-.022z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E") !important;
}
/**
* Link items with icon or checkboxes
*/
.nav-link-flex {
display: flex;
justify-content: space-between;
}
/**
* Grayed out CTA's
*/
.nav-link-dull {
color: var(--gray-45);
}
/**
* Site navigation loading animation
*/
.nav-skeleton {
height: var(--header-height);
margin-inline-end: 15px;
width: 350px;
}
.nav-list .fcc-loader {
padding: 0 40px;
margin-inline-start: 35px;
margin-inline-end: 25px;
}
/**
* Site navigation first level CTA's
*/
.exposed-button-nav,
.lang-button-nav {
border: 1px solid var(--gray-00);
font-size: 18px;
color: var(--gray-00);
background-color: var(--theme-color);
cursor: pointer;
max-height: calc(var(--header-height) - 8px);
display: flex;
align-items: center;
background-color: var(--theme-color);
border: 1px solid var(--gray-00);
color: var(--gray-00);
display: flex;
font-size: 18px;
height: var(--header-element-size);
justify-content: center;
min-width: var(--header-element-size);
padding: 0 4px;
}
.exposed-button-nav {
padding: 2px 14px;
}
.lang-button-nav {
padding: 5px;
@media (min-width: 601px) {
.exposed-button-nav {
padding-inline: 12px;
}
}
.exposed-button-nav:hover,
@@ -228,15 +239,13 @@ li > button.nav-link-signout:not([aria-disabled='true']):is(:hover, :focus) {
border: 1px solid var(--gray-00);
}
.exposed-button-nav:focus {
background-color: var(--gray-90);
color: var(--gray-00);
}
/**
* User thumbnail
*/
.avatar-nav-link {
display: block;
height: 31px;
width: 31px;
height: var(--header-element-size);
width: var(--header-element-size);
}
.avatar-nav-link:hover,
@@ -244,10 +253,6 @@ li > button.nav-link-signout:not([aria-disabled='true']):is(:hover, :focus) {
background-color: var(--theme-color);
}
.avatar-nav-link .avatar-container {
height: 100%;
}
.avatar-nav-link .avatar-container svg,
.avatar-nav-link .avatar-container img {
height: 100%;
@@ -255,61 +260,87 @@ li > button.nav-link-signout:not([aria-disabled='true']):is(:hover, :focus) {
width: 100%;
}
.avatar-container {
border: 2px solid var(--gray-15); /* default border */
height: 100%;
}
/**
* Borders modifiers inidicating user type
*/
.gold-border {
border: 2px solid var(--yellow-gold);
border-color: var(--yellow-gold);
}
.blue-border {
border: 2px solid var(--blue-mid);
border-color: var(--blue-mid);
}
.purple-border {
border: 2px solid var(--purple-mid);
border-color: var(--purple-mid);
}
.default-border {
border: 2px solid var(--gray-15);
}
.expand-nav {
min-height: calc(var(--header-height) * 2);
border-color: var(--gray-15);
}
.display-menu {
display: inherit;
text-align: left;
top: var(--header-height);
max-height: calc(100vh - var(--header-height));
overflow-y: scroll;
-ms-overflow-style: none;
display: block;
max-height: calc(
100vh - var(--header-height) - var(--header-sub-element-size)
);
overflow-y: auto;
scrollbar-width: none;
text-align: left;
top: calc(var(--header-height) + var(--header-sub-element-size));
}
@media (min-width: 980px) {
.display-menu {
max-height: calc(100vh - var(--header-height));
top: var(--header-height);
}
}
.display-menu::-webkit-scrollbar {
display: none;
}
/**
* Sub menu dividers
*/
.nav-line {
border-top: 0.1rem solid var(--gray-45);
border-top: 0.0625rem solid var(--gray-45);
}
/**
* Sign up CTA
*/
.signup-btn {
max-height: calc(var(--header-height) - 6px);
padding: 4px 12px;
margin-inline-start: 2px;
display: flex;
align-items: center;
display: flex;
justify-content: center;
max-height: var(--header-element-size);
min-width: var(--header-element-size);
padding: 0 4px;
}
@media (min-width: 601px) {
.signup-btn {
padding-inline: 12px;
}
}
/**
* Search bar variations (small viewports)
*/
.universal-nav-right .fcc_searchBar {
background-color: var(--theme-color);
height: var(--header-sub-element-size);
left: 0;
position: absolute;
top: var(--header-height);
left: 0;
}
.universal-nav-right .fcc_searchBar .ais-SearchBox-form {
max-width: unset;
}
/* In mobile layout, prevent search input from hanging around if the
@@ -321,7 +352,7 @@ li > button.nav-link-signout:not([aria-disabled='true']):is(:hover, :focus) {
}
/* In mobile layout, prevent search results from hanging around if the
menu is collapsed. */
menu is collapsed. */
.universal-nav-right
#toggle-button-nav[aria-expanded='false']
+ .fcc_searchBar
@@ -329,102 +360,54 @@ li > button.nav-link-signout:not([aria-disabled='true']):is(:hover, :focus) {
display: none;
}
.universal-nav-right .ais-SearchBox-form {
width: calc(100vw - 17rem);
margin-inline-start: 15px;
}
.universal-nav-right .fcc_searchBar .ais-Hits {
width: calc(100vw - 17rem);
}
.ais-SearchBox-input:focus {
box-sizing: content-box;
margin-inline-start: -30px;
padding-inline: 35px;
}
#toggle-button-nav .menu-btn-text,
#universal-nav .login-btn-text {
display: inline-block;
}
#toggle-button-nav .menu-btn-icon,
/**
* Text and icon handling for first level CTA's
*
* Login btn icon is used in other places different from the site navigation
* and is expected to not to be displayed. The default is to hide the element
* but on the site navigation display it only on small viewports.
*/
.login-btn-icon {
display: none;
}
@media (min-width: 980px) {
#universal-nav-logo {
height: 100%;
padding-top: 0;
margin-inline: 1em;
}
.expand-nav {
min-height: var(--header-height);
}
.universal-nav-right {
flex: 1 0 33%;
}
#toggle-button-nav .menu-btn-text,
#universal-nav .login-btn-text {
display: none;
}
@media (max-width: 980px) {
.display-search {
display: initial;
}
.fcc_searchBar .ais-SearchBox-form {
max-width: calc(100vw - 350px);
}
}
@media (max-width: 600px) {
.nav-list {
min-width: 100%;
margin-top: 0;
}
.fcc_searchBar,
.fcc_searchBar div {
width: 100%;
}
.display-menu {
top: calc(var(--header-height) * 2);
max-height: calc(100vh - var(--header-height) * 2);
}
.universal-nav-right .fcc_searchBar .ais-Hits,
.universal-nav-right .fcc_searchBar .ais-SearchBox-form {
width: calc(100% - 30px);
}
#universal-nav .signup-btn {
padding: 4px 6px;
}
@media (min-width: 601px) {
#toggle-button-nav .menu-btn-text,
#universal-nav .login-btn-text {
display: none;
}
#toggle-button-nav .menu-btn-icon,
#universal-nav .login-btn-icon {
display: inline-block;
}
.exposed-button-nav {
padding: 2px 8px;
}
#universal-nav-logo {
padding-block: 0.2em 0.1em;
}
#toggle-button-nav .menu-btn-icon,
#universal-nav .login-btn-icon {
display: inline-block;
}
@media (min-width: 601px) {
#toggle-button-nav .menu-btn-icon,
#universal-nav .login-btn-icon {
display: none;
}
}
@media (max-width: 450px) {
.universal-nav {
padding: 0 5px;
}
.universal-nav {
padding: 0 5px;
}
/**
* Handle submenu containers collapsed and expanded states
*/
button[aria-expanded='false'] + div {
display: none;
}
button[aria-expanded='true'] + div {
display: block;
}
@@ -49,19 +49,13 @@ const UniversalNav = ({
return (
<nav
aria-label={t('aria.primary-nav')}
className={`universal-nav${displayMenu ? ' expand-nav' : ''}`}
className='universal-nav'
id='universal-nav'
>
{isSearchExposedWidth && (
<div
className={`universal-nav-left${
displayMenu ? ' display-search' : ''
}`}
>
{search}
</div>
<div className='universal-nav-left'>{search}</div>
)}
<Link id='universal-nav-logo' to='/learn'>
<Link className='universal-nav-logo' id='universal-nav-logo' to='/learn'>
<NavLogo />
</Link>
<div className='universal-nav-right main-nav'>
@@ -87,9 +81,7 @@ const UniversalNav = ({
showMenu={showMenu}
user={user}
/>
<div className='navatar'>
<AuthOrProfile user={user} />
</div>
<AuthOrProfile user={user} />
</>
)}
</div>
+6 -4
View File
@@ -1,9 +1,10 @@
header {
top: 0;
.site-header {
position: fixed;
top: 0;
width: 100%;
z-index: 200;
z-index: var(--z-index-site-header);
}
.skip-to-content-button {
position: absolute;
top: var(--header-height);
@@ -13,6 +14,7 @@ header {
padding-inline: 1.5em;
left: -1000px;
}
.skip-to-content-button:focus {
left: 0px;
left: 0;
}
+1 -1
View File
@@ -81,7 +81,7 @@ class Header extends React.Component<Props, { displayMenu: boolean }> {
const { displayMenu } = this.state;
const { examInProgress, fetchState, user, skipButtonText } = this.props;
return (
<header>
<header className='site-header'>
<a href='#content-start' className='skip-to-content-button'>
{skipButtonText}
</a>
-4
View File
@@ -1,7 +1,3 @@
:root {
--header-height: 38px;
}
html {
height: 100%;
font-size: 18px;
@@ -31,6 +31,10 @@
--focus-outline-color: var(--blue-mid);
--font-family-sans-serif: 'Lato', sans-serif;
--font-family-monospace: 'Hack-ZeroSlash', monospace;
--header-element-size: 30px;
--header-sub-element-size: 45px;
--header-height: 56px;
--z-index-site-header: 200;
}
.dark-palette {
@@ -589,6 +589,8 @@ a[class^='ais-'] {
.ais-SearchBox-form {
display: block;
position: relative;
width: 100%;
height: var(--header-element-size);
}
.ais-SearchBox-input {
-webkit-appearance: none;
@@ -645,7 +647,8 @@ a[class^='ais-'] {
}
.ais-Hits {
position: absolute;
width: 90%;
left: 0;
right: 0;
background-color: #fff;
}
.ais-Hits-item {
@@ -1,6 +1,17 @@
.fcc_searchBar {
flex-grow: 1;
max-height: 33px;
border-bottom: 0.0625rem solid var(--gray-45);
height: var(--header-sub-element-height);
padding: 6px 15px;
width: 100%;
}
@media (min-width: 980px) {
.fcc_searchBar {
border: 0;
height: auto;
max-width: 500px;
padding: 0;
}
}
.fcc_searchBar strong,
@@ -42,10 +53,6 @@
color: var(--gray-00);
}
.fcc_searchBar .ais-Hits {
left: 15px;
}
/* hits */
.fcc_searchBar .ais-Highlight-highlighted {
background-color: transparent;
@@ -124,13 +131,17 @@ and arrow keys */
}
.fcc_searchBar .ais-SearchBox-form {
display: grid;
grid-template-columns: 26px auto 36px;
grid-template-areas: 'submit input reset';
margin-bottom: 0;
gap: 0.25em;
margin-top: 6px;
align-content: center;
background-color: var(--gray-75);
display: grid;
gap: 0.25em;
grid-template-areas: 'submit input reset';
grid-template-columns: 26px auto 36px;
margin-bottom: 0;
}
.fcc_search_wrapper {
position: relative;
}
.ais-SearchBox-input {
@@ -145,22 +156,16 @@ and arrow keys */
grid-area: reset;
}
@media (min-width: 981px) {
@media (min-width: 980px) {
.ais-SearchBox-input {
width: 100%;
max-width: 100%;
}
.fcc_searchBar {
position: relative;
max-width: 500px;
}
.fcc_searchBar .ais-Hits {
top: auto;
width: 100%;
max-width: 100%;
left: 0px;
}
.fcc_searchBar .ais-SearchBox-form {
top: auto;
}
}
+1 -1
View File
@@ -133,7 +133,7 @@
}
.certification-namespace .ms-logo svg {
width: 300px;
width: 220px;
}
.certification-namespace .information {
@@ -84,8 +84,8 @@ describe('Navbar Logged in', () => {
cy.get(navBarselectors.defaultAvatar).should('exist');
});
it('Should have a profile image with dimensions that are <= 31px', () => {
cy.get(navBarselectors.avatarImage).invoke('width').should('lte', 31);
cy.get(navBarselectors.avatarImage).invoke('height').should('lte', 31);
it('Should have a profile image with dimensions that are <= 26px', () => {
cy.get(navBarselectors.avatarImage).invoke('width').should('lte', 26);
cy.get(navBarselectors.avatarImage).invoke('height').should('lte', 26);
});
});
@@ -381,11 +381,10 @@ html {
}
body {
background-color: var(--secondary-background);
height: 100%;
font-family: var(--font-family-sans-serif);
color: var(--secondary-color);
background: var(--secondary-background);
--header-height: 38px;
}
header {