Animate dialog overflows with visible scrolls - html

I have created a dialog that slides out on closing if on mobile view.
Otherwise it just scales backwards.
This component is based on the awesome work from here:
https://web.dev/building-a-dialog-component/#styling-with-open-props
With some tweeks of my own to adapt it to react and my own needs.
This will add scrollbar on html body for a breef second.
This is super annoying and only fix that works is to hide scrollbar on the html-tag.
html {
box-sizing: border-box;
margin: 0px; padding: 0px
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
}
I do not like this at all, are there any better solutions for this?
import { useEffect, useState, useRef } from "react";
import styled, { keyframes } from "styled-components";
import PropTypes from "prop-types";
import { shadow } from "shared/theme";
import { spacingIndex } from "shared/spacing";
const scaleDown = keyframes`
to { transform: scale(.75) }
`;
const slideOutDown = keyframes`
to { transform: translateY(100%) }
`;
const slideInUp = keyframes`
from { transform: translateY(100%) }
`;
const Container = styled.dialog`
display: ${({ open }) => (open ? "flex" : "none")};
justify-content: center;
align-items: center;
border-radius: 8px;
border: none;
max-inline-size: min(90vw, 60ch);
max-block-size: min(80vh, 100%);
overflow: hidden;
margin: auto;
padding: ${spacingIndex * 2}px;
inset: 0;
box-shadow: ${shadow.overlay};
transition: opacity 0.5s cubic-bezier(0.25, 0, 0.3, 1);
animation: ${scaleDown} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
animation-timing-function: cubic-bezier(0.5, -0.5, 0.1, 1.5);
&:is([open]) {
animation: ${slideInUp} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
}
&:not([open]) {
pointer-events: none;
opacity: 0;
}
::backdrop {
backdrop-filter: blur(4px);
transition: backdrop-filter 0.5s ease;
}
#media (max-width: 768px) {
animation: ${slideOutDown} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
margin-block-end: 16px;
}
`;
const Dialog = ({ isOpened, onClose, children }) => {
const ref = useRef(null);
const [open, setOpen] = useState(false);
const isClickOutsideOfDialog = (dialogEl, event) => {
if (!dialogEl) return false;
const rect = dialogEl.getBoundingClientRect();
return (
event.clientY < rect.top ||
event.clientY > rect.bottom ||
event.clientX < rect.left ||
event.clientX > rect.right
);
};
const onDialogContainerClick = (event) => {
if (isClickOutsideOfDialog(ref.current, event)) onClose();
};
const handleAnimationEnd = (e) => {
if (slideInUp.name !== e.animationName) setOpen(false);
};
useEffect(() => {
ref.current?.addEventListener("animationend", handleAnimationEnd);
return () => ref.current?.removeEventListener("animationend", handleAnimationEnd);
}, []);
useEffect(() => {
if (!isOpened) ref.current?.close();
if (isOpened) {
setOpen(true);
ref.current?.showModal();
}
}, [isOpened]);
return (
<Container
{...{
ref,
open,
onClick: onDialogContainerClick,
}}>
{children}
</Container>
);
};
Dialog.propTypes = {
isOpened: PropTypes.bool,
onClose: PropTypes.func,
};
export default Dialog;
Edit: Guess I was tired and stupid..Just added position:fixed; because I then move the dialog out of the document flow.

Related

Facing difficulty in Implementing Carousel when iterating through an array

I want to implement carousel using data from my Vidoes Array, i am unable to obtain desired output instead all the videos in the Array are rendered at the same time
<div id="carouselExampleSlidesOnly" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-inner">
{Videos}
</div>
</div>
This is my Videos array
const Videos = VIDEOS.map((vid) => {
if(vid.name=="Video1"){
vari=true;
}
return (
<div
className={vari?"carousel-item active":"carousel-item"}
style={{ width: "25vw", height: "5vh", background: "#F1F1F1",margin:"auto" }}
>
<img src="..." class="card-img-top" alt={vid.name}></img>
</div>
);
});
I've done just this a couple of days ago.. Have yet to shorten, optimize and add video support though
here's my code:
import React, { useEffect, useState } from 'react';
import "./MediaViewer.css";
import { useGlobalObj, globalObj } from 'logic/zergski-global-object';
// content JSON
import { mediaJSON } from 'logic/content-collections';
// components
import Anchor from 'shared/Anchor';
let current = 'can1';
let canvas = {
can1: {
pos: 'center',
trans: true,
source: mediaJSON[0].src,
},
can2: {
pos: 'right',
trans: true,
source: mediaJSON[1].src,
},
can3: {
pos: 'left',
trans: false,
source: mediaJSON[mediaJSON.length-1].src,
}
}
// iterator
// TODO! add settings
const iterator = ( array, setState, initIndex ) => {
let index = initIndex;
let count = array.length - 1; // content count
let nextSource = index+2; // next imagesource to load
let nextCanvas = 0; // canvas position
let nextImageSource = nextCanvas - 1;
return {
next() {
index = index === count ? 0 : ++index;
nextSource = index+1 > count ? 0 : index+1;
Object.entries(canvas).forEach( c => {
canvas[c[0]].pos = c[1].pos === 'center' ? 'left' : c[1].pos === 'right' ? 'center' : 'right';
c[1].pos === 'center' && (current = c[0]);
if ( c[1].pos === 'right' ) {
canvas[c[0]].trans = false;
canvas[c[0]].source = mediaJSON[nextSource].src;
} else {
canvas[c[0]].trans = true;
}
});
setState({
...canvas,
counter: index+1,
});
},
prev() {
index = index === 0 ? count : --index;
nextSource = index-1 < 0 ? count : index-1;
Object.entries(canvas).forEach( c => {
canvas[c[0]].pos = c[1].pos === 'center' ? 'right' : c[1].pos === 'left' ? 'center' : 'left';
c[1].pos === 'center' && (current = c[0]);
if ( c[1].pos === 'left' ) {
canvas[c[0]].trans = false;
canvas[c[0]].source = mediaJSON[nextSource].src;
} else {
canvas[c[0]].trans = true;
}
});
setState({
...canvas,
counter: index+1,
});
}
}
}
var lastTouch = 0;
var content = Object.create(null);
// component for viewing images, videos and documents
const MediaViewer = props => {
const mediaViewer = {
display: 'hidden',
contentIndex: 0,
initialState: { display: 'hidden' },
}
const [ viewerState, setViewerState ] = useGlobalObj({ mediaViewer });
const [ state, setState ] = useState({
...canvas,
counter: 1,
});
const [ touchState, setTouchState ] = useState({
touch: false,
movement: 0,
});
if ( viewerState.display === 'show' ) {
document.body.classList.add('scroll-lock');
setTimeout(()=>{
globalObj.mediaViewer.setState({ display: 'visible' });
}, 50)
}
useEffect(() => {
const { display, index } = viewerState;
if ( display === 'show' ) {
content = iterator( mediaJSON, setState, index );
current = 'can1';
canvas = {
can1: {
pos: 'center',
trans: false,
source: mediaJSON[index].src,
},
can2: {
pos: 'right',
trans: false,
source: mediaJSON[index === mediaJSON.length-1 ? 0 : index+1].src,
},
can3: {
pos: 'left',
trans: false,
source: mediaJSON[index === 0 ? mediaJSON.length-1 : index-1].src,
}
}
setState({
...canvas,
counter: index+1,
})
}
}, [viewerState]);
const handleClose = () => {
globalObj.mediaViewer.setState({ display: 'hide' });
setTimeout(()=>{
globalObj.mediaViewer.setState({ display: 'hidden' });
}, 400)
document.body.classList.remove('scroll-lock');
}
// input handlers
const handleMovement = ( delta, touch=false ) => {
let nextMovement = touchState.movement - delta;
canvas[current].trans = false;
canvas[current].movement = nextMovement;
setTouchState({ movement: nextMovement });
}
// touch
const handleTouchStart = (e) => {
lastTouch = e.nativeEvent.touches[0].clientX;
}
const handleTouchMove = (e) => {
const delta = lastTouch - e.nativeEvent.touches[0].clientX;
lastTouch = e.nativeEvent.touches[0].clientX;
handleMovement(Math.floor(delta), true);
}
const handleTouchEnd = (e) => {
touchState.movement < -50 ? content.next() : touchState.movement > 50 && content.prev();
lastTouch = 0;
canvas[current].trans = true;
setTouchState({ movement: 0 });
}
return (
<div className={ `Media-Viewer ${ viewerState.display }` }
onTouchStart={ handleTouchStart }
onTouchMove={ handleTouchMove }
onTouchEnd={ handleTouchEnd }
>
<img className={ `Media-Viewer-Content ${ state.can1.pos }` } alt={ '' }
src={ state.can1.source }
style={{
transform: current === 'can1' ? `translateX(${ touchState.movement }px)` : '',
transitionDuration: `${ canvas.can1.trans ? '' : '0s' }`,
}}
/>
<img className={ `Media-Viewer-Content ${ state.can2.pos }` } alt={ '' }
src={ state.can2.source }
style={{
transform: current === 'can2' ? `translateX(${ touchState.movement }px)` : '',
transitionDuration: `${ canvas.can2.trans ? '' : '0s' }`,
}}
/>
<img className={ `Media-Viewer-Content ${ state.can3.pos }` } alt={ '' }
src={ state.can3.source }
style={{
transform: current === 'can3' ? `translateX(${ touchState.movement }px)` : '',
transitionDuration: `${ canvas.can3.trans ? '' : '0s' }`,
}}
/>
<div className="Modal-User-Interface-Wrapper">
<div className="Information-Box">
<h4>{ `${ state.counter }/${ mediaJSON.length }` }</h4>
</div>
<Anchor
altClass="icon"
fileName="cross.svg"
style={{ position: 'absolute', top: '0', right: '0', opacity: '.5' }}
clicked={ handleClose }
/>
<Anchor
altClass="icon"
fileName="arrow.svg"
style={{ transform: 'rotate(180deg)' }}
clicked={ ()=>content.prev() }
/>
<Anchor
altClass="icon"
fileName="arrow.svg"
clicked={ ()=>content.next() }
/>
</div>
</div>
);
}
export default MediaViewer;
And CSS
.Media-Viewer {
display: flex;
position: fixed;
justify-content: space-around;
align-items: center;
height: calc(100*var(--vh));
width: 100vw;
height: 100vh;
top: 0; left: 0;
overflow: hidden;
z-index: 10;
transition: opacity .4s ease;
}
.Media-Viewer.hide,
.Media-Viewer.show {
opacity: 0;
}
.Media-Viewer.hidden {
display: none;
}
.Media-Viewer-Content {
display: flex;
position: absolute;
height: 100%;
width: 100%;
object-fit: contain;
top: 0; left: 0;
z-index: 9;
transition: transform .4s ease;
will-change: transform;
}
.Media-Viewer-Content.center {
transform: translate3d(0,0,0);
}
.Media-Viewer-Content.right {
transform: translate3d(100vw,0,0);
}
.Media-Viewer-Content.left {
transform: translate3d(-100vw,0,0);
}
.Modal-User-Interface-Wrapper {
display: flex;
position: relative;
justify-content: space-between;
align-items: center;
width: calc( 100vw - (100vw - 300px) / 10 );
opacity: .6;
height: calc(100*var(--vh));
z-index: 10;
transition: opacity 1.2s ease;
transition-delay: 4s;
}
.Media-Viewer .Anchor {
display: flex;
position: relative;
height: 3.5rem;
width: 3.5rem;
padding: 1rem .7rem 1rem 1rem;
opacity: .1;
margin-bottom: 1.7rem;
border-radius: 4rem;
cursor: pointer;
transition: opacity, background-color .7s ease;
z-index: 10;
transition-duration: .44s;
}
.Media-Viewer .Anchor:hover,
.Media-Viewer:active .Modal-User-Interface-Wrapper,
.Media-Viewer-Content:active .Modal-User-Interface-Wrapper {
opacity: .9;
transition-duration: .1s;
transition-delay: 0s;
}
.Media-Viewer .Anchor:hover {
opacity: .7;
background-color:rgba(0, 0, 0, 0.089);
}
/* overlay */
.Media-Viewer::before {
content: '';
position: absolute;
height: 100vh;
width: 100vw;
top: 0;
z-index: 8;
background-color: rgba(0, 0, 0, 0.932);
backdrop-filter: blur(7px);
-webkit-backdrop-filter: blur(10px);
z-index: -1;
}
.Information-Box {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: .4rem; right: 0; left: 0;
margin: 0 auto;
height: 2.7rem;
width: calc(3rem + 10vw);
background: rgb(220,148,87);
background: linear-gradient(133deg, rgba(32, 28, 24, 0.719) 0%, rgba(43, 32, 26, 0.582) 100%);
border-radius: .6rem;
z-index: 10;
}
.Information-Box h4 {
position: absolute;
color: #fff;
font-size: .85rem;
font-weight: 700;
text-shadow: 1px 2px 5px rgb(1, 4, 5);
}
Maybe you'll find something useful here..

Button Loading react-hooks-nextjs

I am doing a loading spinner button, When I press the button, the loading icon appears but does not disappear at the marked time, and continues with the load. Here is the code:
componentOne:
import React from 'react'
function Loader(){
return(
<>
<div className="loaderIcon"></div>
<style jsx>
{`
.loaderIcon{
border: 10px solid #f3f3f3;
border-radius: 50%;
border-top: 10px solid #3498bd;
width: 60px;
height: 60px;
animation: spin 2s linear infinite;
}
#keyframes spin{
0% { transform: rotate(0deg);}
100% { transform rotate(360deg);}
}
`}
</style>
</>
)
}
export default Loader;
componentTwo:
import React, {useState} from 'react';
import Loader from '../components/loader'
function ButtonLoading(){
const [loading, setLoading] = useState(false)
function loadData(){
setLoading ({loading: false})
setTimeout(()=>{
setLoading({loading: true});
}, 1000);
}
return(
<>
<div>
<button className="btnLoading" onClick={loadData} disabled={loading}>
{loading &&(<Loader/>)}
{loading && <span className="oneSpan">Loading page</span>}
{!loading && <span className="twoSpan">Load page</span>}
</button>
</div>
<style jsx>
{`
.btnLoading{
background-color: green;
}
.oneSpan{
color: red;
font-size: 20px;
}
.twoSpan{
color: black;
font-size: 20px;
}
`}
</style>
</>
)
}
export default ButtonLoading;
I think you got the loading backwards. I believe the function should look like `
function loadData(){
setLoading ({loading: true})
setTimeout(()=>{
setLoading({loading: false});
}, 1000);
}`
SetTimeout runs the function AFTER the timer is up

How can I make this nav bar thinner and the background color transparent

the code: https://codesandbox.io/s/goofy-boyd-xscgd
live demo: https://xscgd.csb.app/
I have a nav bar on the page which will show up when you are scrolling up on the page.
The component's code is
import React, { Component } from "react";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import AnchorLink from "react-anchor-link-smooth-scroll";
import { navLinks, navHeight } from "./config";
import Menu from "./Menu";
import styled from "styled-components";
import theme from "../styles/theme";
import media from "../styles/media";
import mixins from "../styles/mixins";
const { colors, fontSizes, fonts } = theme;
const throttle = (func, wait = 100) => {
let timer = null;
return function(...args) {
if (timer === null) {
timer = setTimeout(() => {
func.apply(this, args);
timer = null;
}, wait);
}
};
};
const NavContainer = styled.header`
${mixins.flexBetween};
position: fixed;
top: 0;
padding: 0px 50px;
background-color: ${colors.navy};
transition: ${theme.transition};
z-index: 11;
filter: none !important;
pointer-events: auto !important;
user-select: auto !important;
width: 100%;
height: ${props =>
props.scrollDirection === "none" ? theme.navHeight : theme.navScrollHeight};
box-shadow: ${props =>
props.scrollDirection === "up"
? `0 10px 30px -10px ${colors.shadowNavy}`
: "none"};
transform: translateY(
${props =>
props.scrollDirection === "down" ? `-${theme.navScrollHeight}` : "0px"}
);
${media.desktop`padding: 0 40px;`};
${media.tablet`padding: 0 25px;`};
`;
const Navbar = styled.nav`
${mixins.flexBetween};
position: relative;
width: 100%;
color: ${colors.lightestSlate};
font-family: ${fonts.SFMono};
counter-reset: item 0;
z-index: 12;
`;
const Logo = styled.div`
${mixins.flexCenter};
`;
const LogoLink = styled.a`
display: block;
color: ${colors.green};
width: 42px;
height: 42px;
&:hover,
&:focus {
svg {
fill: ${colors.transGreen};
}
}
svg {
fill: none;
transition: ${theme.transition};
user-select: none;
}
`;
const Hamburger = styled.div`
${mixins.flexCenter};
overflow: visible;
margin: 0 -12px 0 0;
padding: 15px;
cursor: pointer;
transition-timing-function: linear;
transition-duration: 0.15s;
transition-property: opacity, filter;
text-transform: none;
color: inherit;
border: 0;
background-color: transparent;
display: none;
${media.tablet`display: flex;`};
`;
const HamburgerBox = styled.div`
position: relative;
display: inline-block;
width: ${theme.hamburgerWidth}px;
height: 24px;
`;
const HamburgerInner = styled.div`
background-color: ${colors.green};
position: absolute;
width: ${theme.hamburgerWidth}px;
height: 2px;
border-radius: ${theme.borderRadius};
top: 50%;
left: 0;
right: 0;
transition-duration: 0.22s;
transition-property: transform;
transition-delay: ${props => (props.menuOpen ? `0.12s` : `0s`)};
transform: rotate(${props => (props.menuOpen ? `225deg` : `0deg`)});
transition-timing-function: cubic-bezier(
${props =>
props.menuOpen ? `0.215, 0.61, 0.355, 1` : `0.55, 0.055, 0.675, 0.19`}
);
&:before,
&:after {
content: "";
display: block;
background-color: ${colors.green};
position: absolute;
left: auto;
right: 0;
width: ${theme.hamburgerWidth}px;
height: 2px;
transition-timing-function: ease;
transition-duration: 0.15s;
transition-property: transform;
border-radius: 4px;
}
&:before {
width: ${props => (props.menuOpen ? `100%` : `120%`)};
top: ${props => (props.menuOpen ? `0` : `-10px`)};
opacity: ${props => (props.menuOpen ? 0 : 1)};
transition: ${props =>
props.menuOpen ? theme.hamBeforeActive : theme.hamBefore};
}
&:after {
width: ${props => (props.menuOpen ? `100%` : `80%`)};
bottom: ${props => (props.menuOpen ? `0` : `-10px`)};
transform: rotate(${props => (props.menuOpen ? `-90deg` : `0`)});
transition: ${props =>
props.menuOpen ? theme.hamAfterActive : theme.hamAfter};
}
`;
const NavLinks = styled.div`
display: flex;
align-items: center;
${media.tablet`display: none;`};
`;
const NavList = styled.ol`
div {
${mixins.flexBetween};
}
`;
const NavListItem = styled.li`
margin: 0 10px;
position: relative;
font-size: ${fontSizes.smallish};
counter-increment: item 1;
&:before {
content: "0" counter(item) ".";
text-align: right;
color: ${colors.green};
font-size: ${fontSizes.xsmall};
}
`;
const NavLink = styled(AnchorLink)`
padding: 12px 10px;
`;
const ResumeLink = styled.a`
${mixins.smallButton};
margin-left: 10px;
font-size: ${fontSizes.smallish};
`;
const DELTA = 5;
class Nav extends Component {
state = {
isMounted: false,
menuOpen: false,
scrollDirection: "none",
lastScrollTop: 0
};
componentDidMount() {
setTimeout(() => this.setState({ isMounted: true }), 100);
window.addEventListener("scroll", () => throttle(this.handleScroll()));
window.addEventListener("resize", () => throttle(this.handleResize()));
window.addEventListener("keydown", e => this.handleKeydown(e));
}
componentWillUnmount() {
this.setState({ isMounted: false });
window.removeEventListener("scroll", () => this.handleScroll());
window.removeEventListener("resize", () => this.handleResize());
window.removeEventListener("keydown", e => this.handleKeydown(e));
}
toggleMenu = () => this.setState({ menuOpen: !this.state.menuOpen });
handleScroll = () => {
const { isMounted, menuOpen, scrollDirection, lastScrollTop } = this.state;
const fromTop = window.scrollY;
// Make sure they scroll more than DELTA
if (!isMounted || Math.abs(lastScrollTop - fromTop) <= DELTA || menuOpen) {
return;
}
if (fromTop < DELTA) {
this.setState({ scrollDirection: "none" });
} else if (fromTop > lastScrollTop && fromTop > navHeight) {
if (scrollDirection !== "down") {
this.setState({ scrollDirection: "down" });
}
} else if (fromTop + window.innerHeight < document.body.scrollHeight) {
if (scrollDirection !== "up") {
this.setState({ scrollDirection: "up" });
}
}
this.setState({ lastScrollTop: fromTop });
};
handleResize = () => {
if (window.innerWidth > 768 && this.state.menuOpen) {
this.toggleMenu();
}
};
handleKeydown = e => {
if (!this.state.menuOpen) {
return;
}
if (e.which === 27 || e.key === "Escape") {
this.toggleMenu();
}
};
render() {
const { isMounted, menuOpen, scrollDirection } = this.state;
return (
<NavContainer scrollDirection={scrollDirection}>
<Navbar>
<TransitionGroup>
{isMounted && (
<CSSTransition classNames="fade" timeout={3000}>
<Hamburger onClick={this.toggleMenu}>
<HamburgerBox>
<HamburgerInner menuOpen={menuOpen} />
</HamburgerBox>
</Hamburger>
</CSSTransition>
)}
</TransitionGroup>
<NavLinks>
<NavList>
<TransitionGroup>
{isMounted &&
navLinks &&
navLinks.map(({ url, name }, i) => (
<CSSTransition key={i} classNames="fadedown" timeout={3000}>
<NavListItem
key={i}
style={{ transitionDelay: `${i * 100}ms` }}
>
<NavLink href={url}>{name}</NavLink>
</NavListItem>
</CSSTransition>
))}
</TransitionGroup>
</NavList>
<TransitionGroup>
{isMounted && (
<CSSTransition classNames="fadedown" timeout={3000}>
<div style={{ transitionDelay: `600ms` }}>
<ResumeLink
href="/resume.pdf"
target="_blank"
rel="nofollow noopener noreferrer"
>
Resume
</ResumeLink>
</div>
</CSSTransition>
)}
</TransitionGroup>
</NavLinks>
</Navbar>
<Menu menuOpen={menuOpen} toggleMenu={this.toggleMenu} />
</NavContainer>
);
}
}
export default Nav;
The problem is really simple though.
As you can see, when viewing the page through mobile devices, there will be a thick nav bar sticking at the top. I really want to make it thinner(or shorter) and also make its background to transparent. However I tried to find where I should change the code but I couldn't. I am not really good at CSS and hope someone could point out the part of the code I should change to fix this problem.
Note that I only want to change the nav bar for mobile devices.
you need to add breakpoint on your styled NavContainer like this:
const NavContainer = styled.header`
${mixins.flexBetween};
position: fixed;
top: 0;
padding: 0px 50px;
background-color: ${colors.navy};
transition: ${theme.transition};
z-index: 11;
filter: none !important;
pointer-events: auto !important;
user-select: auto !important;
width: 100%;
height: ${props =>
props.scrollDirection === "none" ? theme.navHeight : theme.navScrollHeight};
box-shadow: ${props =>
props.scrollDirection === "up"
? `0 10px 30px -10px ${colors.shadowNavy}`
: "none"};
transform: translateY(
${props =>
props.scrollDirection === "down" ? `-${theme.navScrollHeight}` : "0px"}
);
${media.desktop`padding: 0 40px;`};
${media.tablet`padding: 0 25px;`};
#media (max-width: 768px) {
background-color: rgba(255,255,255,0.5);
height: auto;
}
`;
Seems like you're looking for Media Queries.
They don't appear to be supported with ReactJS in the way you're making variables to store your styles but there is a library that lets you use media queries.
See - https://github.com/ReactTraining/react-media
For more information on media queries in general see - https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
Essentially you look at the width of the device and then specify a style based on that. Super useful for determining if a client is using a mobile device or not.
You can setup a media query to check for a width < 600px and then based on that change the navbar to be transparent and thinner.
Hope the above helps!

How can I get rid of these mysterious grey boxes at the margins of a div element?

I am having a mysterious problem that did not exist yesterday (you know those?). For some reason, these grey boxes are appearing at the edges of an element that appears when a user hovers over its parent element. they appear to the right and bottom. Essentially, everything below the check and x images is a div with a class of .bottom. I have set to 50% of parent height and 100% of its width. These are react components, so it may be a tad hard to follow. The EventDetail component is nested in the Event component.
.eventcontainer {
padding: 5px;
width: 960px;
margin: 20px auto;
}
.event {
margin-top: 2%;
width: 960px;
border-color:#496DD9;
border-style: dotted;
font-size: 0.5em;
height: 300px;
position: relative;
-webkit-transition: height 300ms ease-out;
}
.event:hover {
height: 400px;
-webkit-transition: height 300ms ease-in;
}
.event:hover .hidden {
display: block;
opacity: 1;
-webkit-transition: opacity 300ms ease-in 200ms;
}
.hidden {
opacity: 0;
overflow: scroll;
-webkit-transition: opacity 100ms ease-out;
}
.bottom {
position: absolute;
bottom: 0;
padding-bottom: 2%;
padding-left: 2%;
font-size: 13px;
width: 100%;
height: 50%;
}
const EventDetail = (props) => {
const options = {
color: '#DE5745',
trailWidth: 1,
strokeWidth: 10,
};
const containerStyle = {
width: '100px',
height: '20px',
};
const showUpdate = (e) => {
e.preventDefault();
props.dispatch(renderUpdate(props.id));
};
return (
<div className=" bottom hidden" >
<p>Attendance</p>
<div className="progressbar">
<Line progress={(props.attending / props.capacity)} containerStyle={containerStyle} initialAnimate options={options} containerClassName={'.progressbar'} />
<span>{props.attending}</span><span>/</span><span> {props.capacity}</span>
</div>
{/* <div>{props.attendeeHelperFunction(props.attendeeList)}</div>*/}
<p>Description:</p>
{props.description === '___' ? <p>The owner did not provide a description. So mysterious</p> : <p>{props.description}</p>}
{window.localStorage.getItem('userid') === props.owner_id ?
<div>
<Update
event_title={props.title}
event_description={props.description}
event_capacity={props.capacity}
event_id={props.event_id}
/>
</div>
: null}
</div>
);
};
const Event = (props) => {
const attendHandler = (e) => {
e.preventDefault();
props.dispatch(attendEvent(props.id))
.then(() => {
props.dispatch(fetchEvents());
});
};
const flakeHandler = (e) => {
e.preventDefault();
props.dispatch(flakeEvent(props.id))
.then(() => {
props.dispatch(fetchEvents());
})
.catch(err => console.log(err));
};
return (
<div className="event">
<section className="eventdescription">
<p>{props.title}</p>
<p>{props.date}</p>
<p>Hosted by: {props.owner}</p>
<p>{console.log('EVENT', renderEvents)}</p>
</section>
<section className="attend"><img onClick={attendHandler}className="eventgraphic" src={check} alt="" /></section>
<section className="unattend"><img onClick={flakeHandler}className="eventgraphic" src={crossout} alt="" /></section>
<EventDetail
title={props.title}
event_id={props.id}
owner_id={props.owner_id}
attending={props.attendees}
capacity={props.capacity}
description={props.description}
attendeeList={props.attendeeList}
attendeeHelperFunction={props.attendeeHelperFunction}
/>
</div>
);
};
They are scrollbars, duh! But if anyone ever stumbles onto this thread, here is a style generator for scroll bars https://mikethedj4.github.io/Webkit-Scrollbar-Generator/

Keyframes with Inline Styles ReactJS

I'm trying to set the keyframes of a pulsate animation in ReactJS. I tried just setting the keyframes inside the inline style but that doesn't work.
My code
const NewRelpyheButton = ({style = {}, open, handleOPenSettings}) => {
var bar = {
color: '#000',
padding: '1em 0',
fontSize: '20px',
textAlign: 'center',
cursor: 'pointer',
position: 'fixed',
bottom: '0',
width: '100%',
zIndex: '10',
animation: 'pulse 1.2s ease-in-out',
animationIterationCount: 'infinite',
}
Object.assign(style, {});
let openModal;
if (open) {
openModal = <Modal><NewRelpyhe/></Modal>
}
return (
<div>
{openModal}
<Bar color='purple' style={bar} onClick={handleOpenSettings}>
create a new relphye site
</Bar></div>
)
}
I'm trying to mimic this in css:
.element {
width: 100%;
height: 100%;
animation: pulse 5s infinite;
}
#keyframes pulse {
0% {
background-color: #001F3F;
}
100% {
background-color: #FF4136;
}
}
html,
body {
height: 100%;
}
If you like to keep all your styling tightly coupled to your components, give Styled Components a go. They have a helper for keyframes
e.g.
import styled, { keyframes } from 'styled-components'
const pulse = keyframes`
from {
background-color: #001F3F;
}
to {
background-color: #FF4136;
}
`
const Bar = styled.div`
color: #000;
padding: 1em 0;
font-size: 20px,
text-align: center;
cursor: pointer;
position: fixed;
bottom: '0',
width: 100%;
z-index: 10;
animation: ${pulse} 1.2s ease-in-out;
animation-iteration-count: infinite;
`
Then use like so:
<Bar>I pulse</Bar>
Here's how we will achieve it without adding any dependency.
{/*Your Js File Code */}
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import React from "react";
import "./test.css";
class Anim extends React.Component {
constructor(props) {
super(props);
this.state = {
animationName: ""
};
}
addStylesheetRules(rules) {
var styleEl = document.createElement("style");
document.head.appendChild(styleEl);
var styleSheet = styleEl.sheet;
styleSheet.insertRule(rules, 0);
}
clickHdl() {
let animationName = `animation${Math.round(Math.random() * 100)}`;
let keyframes = `
#-webkit-keyframes ${animationName} {
10% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
90% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
100% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
}`;
this.addStylesheetRules(keyframes);
this.setState({
animationName: animationName
});
}
render() {
let style = {
animationName: this.state.animationName,
animationTimingFunction: "ease-in-out",
animationDuration: "0.6s",
animationDelay: "0.0s",
animationIterationCount: 1,
animationDirection: "normal",
animationFillMode: "forwards"
};
return (
<div>
<button type="button" onClick={this.clickHdl.bind(this)}>
Animation!
</button>
<div className="box" style={style}></div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<Anim />
</StrictMode>,
rootElement
);
{/*Css Code test.css */}
.box {
width: 30px;
height: 30px;
background: red;
border-radius: 50%;
cursor: pointer;
}
Demo: https://codesandbox.io/s/reverent-sun-qjo91?file=/src/index.js