How do I make a Material toolbar opaque on scroll and transparent at start? - css

I'm currently trying to familiarise myself with angular. I'm using angular material and I'm looking to make the material toolbar sticky and opaque on scroll and, transparent with toolbar text still visible when at the very top of the page. Everything I've searched for so far involved javascript or jquery. How do I go about it in angular 8 precisely?
This is my HTML & CSS respectively:
<mat-toolbar color="primary">
<a mat-button [routerLink]="['home']" >
<h1>PETER<span class="light">CONSTRUCTION</span></h1>
</a>
<span class="spacer"></span>
<a mat-button [routerLink]="['home']" routerLinkActive="active" >HOME</a>
<a mat-button [routerLink]="['about']" routerLinkActive="active">ABOUT</a>
<a mat-button [routerLink]="['contact']" routerLinkActive="active">CONTACT</a>
</mat-toolbar>
mat-toolbar {
position: absolute;
z-index: 1;
overflow-x: auto;
background-color: #c3cfd2;
}
mat-toolbar-row {
justify-content:space-between;
}
.spacer {
flex: 1 1 auto;
}
a.active {
background-color: rgba(0,0,0, 0.3);
}
h1 {
margin: 0;
color: black;
}
h1 .light {
font-weight: 100;
}
/*.x-bar.x-bar-absolute{*/
/* background-color: hsla(276, 6%, 63%, 0.15) !important;*/
/* transition: none !important;*/
/*}*/
/*.x-bar.x-bar-fixed{*/
/* background-color: hsla(276, 6%, 63%, 1) !important;*/
/*}*/
/*.x-bar [class^="x-bg"] {*/
/* background-color: transparent !important;*/
/*}*/
/*.x-bar.x-bar-absolute .hm5.x-menu > li > .x-anchor .x-anchor-text-primary {*/
/* color: #fff;*/
/*}*/
/*.x-bar.x-bar-fixed .hm5.x-menu > li > .x-anchor .x-anchor-text-primary {*/
/* color: #000;*/
/*}*/

There are multiple ways of achieving this, but since you're already using #angular/material, you can take advantage of the #angular/cdk and it's ScrollDispatchModule (see docs).
It allows you for easy and clean observing of scroll events for registered elements, outside of the NgZone, meaning it will have small impact on the performance.
See the example stackblitz:
https://stackblitz.com/edit/angular-npdbtp
First, you need to import ScrollDispatchModule and register provider for ScrollDispatcher:
import {ScrollDispatchModule, ScrollDispatcher} from '#angular/cdk/scrolling';
#NgModule({
imports: [
(other imports)
ScrollDispatchModule
],
providers: [ScrollDispatcher]
})
export class AppModule {}
Then in your template you can mark an html element with the cdkScrollable directive. This will automatically register it in the ScrollDispatcher.
You can also bind component's style (e.g. opacity) to a property defined in your component:
<div class="scroll-wrapper" cdkScrollable>
<mat-toolbar class="sticky-toolbar" [style.opacity]="opacity">My App</mat-toolbar>
<div>content</div>
</div>
You can make html element sticky using the display: sticky together with top: 0:
.sticky-toolbar {
position: sticky;
top: 0px;
}
Then you will need to inject the ScrollDispatcher and NgZone into your component and define opacity property:
opacity = 1;
constructor(
private scrollDispatcher: ScrollDispatcher,
private zone: NgZone
) {}
Then you can subscribe to scrolled events of the ScrollDispatcher. Those are emitted for all the registered components. You can also register to scroll events of a single element - refer to the docs if needed by.
ngOnInit(): void {
this.scrollDispatcher.scrolled().subscribe((event: CdkScrollable) => {
const scroll = event.measureScrollOffset("top");
let newOpacity = this.opacity;
if (scroll > 0) {
newOpacity = 0.75;
} else {
newOpacity = 1;
}
if (newOpacity !== this.opacity) {
this.zone.run(() => {
this.opacity = newOpacity;
});
}
});
}
The ScrollDispatcher runs outside of NgZone, meaning it will not run change detection in the whole application. This allows for better performance, and it's why we're also injecting NgZone and running the property change inside the zone - this calls the proper change detection along the tree of components.

Related

Trying to display burger menu dropdown behind navbar

I have spent countless hours trying to figure out why my dropdown that is opened/closed by a burger menu icon click is sitting in front of the navbar even though I have specified z-indexes, overflows and positions. This issue is only happening on the MobileNav component below. MobileNav consists of a burger icon and the actual dropdown. Once the burger icon is clicked, the dropdown will either close or open. Currently It is displaying above the nav component and I am having a very hard time figuring out why. Any help will be much appreciated.
Vid to see the dropdown's behavior: https://www.youtube.com/watch?v=zOBnb6r_RN4&ab_channel=TylerOreskey
The dropdown is supposed to come out from the bottom of the navbar and close up into the bottom of the navbar.
Navbar Component: Renders MobileNav component
const Navbar = (props) => {
const [showDropdown, setShowDropdown] = useState(false);
const dropdownToggleHandler = () => setShowDropdown(!showDropdown);
const dropdownClosedHandler = () => setShowDropdown(false);
return (
<header
className={classes.Navbar}
style={{
position: props.passedNavbar ? "fixed" : "relative",
}}
>
<nav className={classes.MobileNav}>
<MobileNav
allNavigationRefs={props.allNavigationRefs}
scrollToDiv={props.scrollToDiv}
open={showDropdown}
closed={dropdownClosedHandler}
dropdownToggleHandler={dropdownToggleHandler}
/>
</nav>
</header>
);
};
export default memo(Navbar);
CSS file for Navbar component: z-index is not working in here.
.Navbar {
top: 0;
height: 50px;
background-color: hsl(213, 27%, 15%);
border-bottom: #00bfff 3px solid;
width: 100%;
z-index: 500;
}
#media (max-width: 500px) {
.DesktopNav {
display: none;
}
}
#media (min-width: 500px) {
.MobileNav {
display: none;
}
}
MobileNav component
const MobileNav = (props) => {
return (
<div className={classes.MobileNav}>
<DropdownToggle clicked={props.dropdownToggleHandler} />
<Dropdown open={props.open} allNavigationRefs={props.allNavigationRefs} />
</div>
);
};
export default MobileNav;
CSS file for MobileNav component
.MobileNav {
overflow: hidden;
}
Dropdown component: (This is displayed above the Navbar component and I cannot get it to be behind the navbar component).
const Dropdown = (props) => {
let attachedClasses = [classes.Dropdown, classes.Close];
if (props.open) {
attachedClasses = [classes.Dropdown, classes.Open];
}
return (
<div className={attachedClasses.join(" ")}>
<NavigationItems allNavigationRefs={props.allNavigationRefs} />
</div>
);
};
export default Dropdown;
CSS file for Dropdown component: z-index is not working in here.
.Dropdown {
background: hsl(212, 87%, 3%);
height: 200px;
transition: transform 0.3s ease-out;
z-index: 400;
display: block;
}
.Open {
transform: translate(0, 25%);
}
.Close {
transform: translate(0, -75%);
}
You are confused on how z-index works.
Consider each level in your tree as a layer.
lets say that Navbar is layer 0, MobileNav is then layer 1, and its children are on layer 2.
By default z-index is calculated among children of the same layer. This is true when the position attribute is on default static. When you alter this to relative you can instruct which layers are going to interuct with each other in a more immediate way.
Having 500 z-index on Navbar will make no sense to MobileNav. It is not his sibling, it's his child.
Here is a possible solution if you can alter the DOM tree
<header
className={classes.Navbar}
style={{
position: props.passedNavbar ? "fixed" : "relative"
}}
>
<nav className={classes.Navbar}>
<DropdownToggle clicked={props.dropdownToggleHandler} />
</nav>
<MobileNav
className={classes.MobileNav}
allNavigationRefs={props.allNavigationRefs}
scrollToDiv={props.scrollToDiv}
open={showDropdown}
closed={dropdownClosedHandler}
/>
</header>
and here is an answer if you can alter the CSS
.Navbar {
top: 0;
height: 50px;
background-color: hsl(213, 27%, 15%);
border-bottom: #00bfff 3px solid;
width: 100%;
position: relative;
}
...
.Dropdown {
background: hsl(212, 87%, 3%);
height: 200px;
transition: transform 0.3s ease-out;
display: block;
position: relative;
z-index: -1;
}

How to dynamically apply CSS in vue.js?

This is my situation:
I have a web app that allow users to change the UI size. (i.e. small, medium or large button)
Users can change how it looks like dynamically during runtime. All text and form input boxes will be resized.
My project is in Vue.js.
What is the best way to solve this? Loading different css when user click?
Load different CSS while user click the button similar to this . Codepen : https://codepen.io/anon/pen/NJEoVM
HTML
<div id="app" :class="size">
<div class="text">Text</div>
<input class="ipt"/><br/><br/>
<button class="btn" #click="change('small')">Small</button>
<button class="btn" #click="change('medium')">Medium</button>
<button class="btn" #click="change('large')">Large</button>
</div>
CSS
.small .ipt{
width: 100px;
height:30px;
}
.small .text{
font-size: 18px;
}
.medium .ipt{
width: 300px;
height:50px;
}
.medium .text{
font-size: 32px;
}
.large .ipt{
width: 600px;
height:100px;
}
.large .text{
font-size: 64px;
}
Javascript
new Vue({
el: '#app',
data:()=>({
size:'small'
}),
methods:{
change(val){
this.size = val
}
}
});
Actually, you can make use of Custom Properties aka CSS variables.
Firstly, define the button CSS styles
/* button.css */
#buttonRef {
--fontSize: 16px;
font-size: var(--fontSize)
}
The overall flow would be something like the following one e.g
methods: {
changeButtonSize: function(size, event) {
event.preventDefault();
/* size might be either of 's', 'm', 'l' */
/* let the button ref is stored in refs */
const buttonRef = this.$refs[“buttonRef”];
let fontSize = 16;
switch(size) {
case 's':
fontSize = 12;
case 'm':
fontSize = 18;
case 'l':
fontSize = 22;
}
/* dynamically change the value for custom property */
buttonRef.style.setProperty("--fontSize", fontSize);
}
}
you can set up 3 classes:
.small {font-size:12px}
.medium {font-size:18px}
.large {font-size:24px}
Then add or remove them to your main parent div onClick.
If you have discreetly set the font-size on the elements, you'll have to target those elements as such:
.small .description { font-size:12px }
.small .title{ font-size:16px }

React - Apply **active** class to an element using CSS modules

I'm diving into React and I found a little problem managing active class names when using CSS modules.
Suppose I want to develop a Tabs React component. I would like to apply an active class to the current list item. The tab headers is built by the following component:
import React, { Component } from 'react';
import styles from './Tabs.scss';
export default class TabHeader extends Component {
render() {
let activeTabIndex = this.props.activeTabIndex;
return (
<ul className={styles['tabs-header']}>
{
this.props.data.map((item, index) => {
return (
<li key={index}>
<a className={(index === activeTabIndex) ? 'active' : ''} href="#">
<span>{item.header}</span>
</a>
</li>
)
})
}
</ul>
);
}
}
As you can see, I conditionally added the class active to the interested list item. The stylesheet code is Tabs.scss:
.tabs-header {
display: table;
width: 100%;
list-style-type: none;
& li {
display: table-cell;
text-align: center;
color: #ECF0F1;
cursor: pointer;
a {
display: block;
padding: 15px;
background: #212F3D;
transition: all .2s ease-in;
transform: skew(-40deg);
&:hover {
background: #2471A3;
color: #F7F9F9;
}
& span {
display: block;
transform: skew(40deg);
}
&.active {
background: #2471A3;
}
}
}
}
With this setup, the active item is not using the active css code. How can I solve the problem?
EDIT: the prop activeTabIndex (integer greater than or equal to zero) is correctly working. If I inspect the elements, I can see the class active being added to the active item list, but it is not pointing to the class defined in Tabs.scss. Just to point this out, when using className={styles['tabs-header']} in the ul element, this is going to be converted to Tabs__tabs-header__2LSPG.
I’d need to try it out to be confident of this, but I think you should be using styles.active instead of 'active’.
this should work<a
className={${index === activeTabIndex && css.active}}
href="#"
{item.header}
Your condition would leave an empty space in a class name because of the condition:
(index === activeTabIndex) ? 'active' : ''
Instead you can do a short-circuit evaluation:
<a className={(index === activeTabIndex) && styles['active']} href="#">
<span>{item.header}</span>
</a>
All about CSS modules in React:
For a className with the dash use: style["upper-level"]
Single word className: style.active
To combine a multiple classNames use: style["upper-level"] + " " + style.active
import style from "./style.module.css";
...
className={
menuOpened
? style["upper-level"] + " " + style.active
: style["upper-level"]
}

Animation not working on component in ReactJS.

Good day. I have been attempting to create the animation demonstrated here in ReactJS. I have using that codepen as a loose reference, yet the animation doesn't work when I press the login button on my own project implementing this effect on the login page.
What have I done wrong?
Thanks in advance,
Duke J. Morgan.
The specific aspects of my project that, as far am I aware, are primary components in the login page that, in this problem, might be the cause of the problem I mentioned above:
import React, { Component } from 'react';
class LoginComponent extends Component {
constructor(props) {
super(props)
this.state = {
incorrect: false
}
}
onLoginButtonClick() {
let passwordInput = document.getElementById("passwordInput");
if (passwordInput.innerHTML !== "test") {
// passwordInput.classList.add("incorrect-login");
let copy = this.state;
copy.incorrect = true;
console.log(`Incorrect boolean: ${copy.incorrect}`);
this.setState(copy);
return;
}
this.props.setPage();
}
render() {
return (<div className="box" id="loginBox">
<h2 className="title">Username</h2>
<input type="text" placeholder="Username" className="input"/>
<h2 className="title">Password</h2>
<input id="passwordInput" placeholder="Password" className={`input ${this.state.incorrect ? 'incorrect-login' : ''}`} type="password"/>
{/* Forgot Password? */}
<button className="button" id="login" onClick={() => {this.onLoginButtonClick()}}>Login</button>
</div>);
}
}
export default LoginComponent;
//The CSS of the login box. incorrect-login is the class added to the password input element when the login button is clicked but the incorrect password, or no password, has been entered.
#loginBox {
.title:nth-child(1) {
margin-bottom: 10px;
}
.title:nth-child(3) {
margin-bottom: 10px;
margin-top: 10px;
}
.incorrect-login {
margin: 4px auto;
width: 70%;
height: 15%;
display: block;
padding: 5px;
padding-left: 10px;
border: 2px solid red;
animation: move 10s;
}
}
#keyframes move {
0%, 100% { left: 0px;}
20% , 60%{left: 15px;}
40% , 80%{left: -15px;}
}
The problem is in the css. I think you should make separated classes for the login and incorrect-login.
.login {
margin: 4px auto;
width: 70%;
height: 15%;
display: block;
padding: 5px;
padding-left: 10px;
position: absolute;
}
.incorrect-login {
border: 2px solid red;
animation: move 10s;
}
If you want to use property "left" in animation you have also add property position: absolute; to the component's class.
And code for the password input class name should be:
className={`input ${this.state.incorrect ? 'login incorrect-login' : 'login'}`}
Also you should not use document.getElementById in React applications directly. If you really need to get reference of the dom element created by React, you should use ref prop instead.

How to set background color to $actionsheet in ionic frameworks

Here is the code, very simple and copy paste from office website
$scope.show = function() {
// Show the action sheet
var hideSheet = $ionicActionSheet.show({
destructiveText: 'Delete Photo',
titleText: 'Modify your album',
cancelText: 'Cancel <i class="icon ion-no-smoking"></i>',
cancel: function() {
// add cancel code..
},
buttonClicked: function(index) {
return true;
}
});
// For example's sake, hide the sheet after two seconds
$timeout(function() {
hideSheet();
}, 2000);
};
I want to change the cancel button have a red color background, how I can achieve it in ionic frameworks?
Easiest way is to look at the markup using your browser (after running ionic serve in your terminal), for example in Chrome ctrl+shift+i, where you can choose the button and see what classes are attached. In your case you'll see something like this:
<div class="action-sheet-group action-sheet-cancel" ng-if="cancelText">
<button class="button ng-binding"
ng-click="cancel()"
ng-bind-html="cancelText">Cancel</button>
</div>
Which has styles that for the parent div, and child button something like this:
.action-sheet-group {
margin-bottom: 8px;
border-radius: 4px;
background-color: #fff;
overflow: hidden;
}
.action-sheet .button {
display: block;
padding: 1px;
width: 100%;
border-radius: 0;
border-color: #d1d3d6;
background-color: transparent;
color: #007aff;
font-size: 21px;
}
Just change these values either in Sass or directly in your styles sheet if you're not using Sass.

Resources