changing CSS of element on page scroll - css

I have a fixed header component as the two pics below illustrate. The current styling that I have is fine for when the page loads and nothing has changed. However, I want the header to have a visible border-bottom of say 1px solid black as soon as the user starts to scroll down the page. So in this case, this styling change would apply to the second pic. How can I accomplish this?
header on page load:
header on scroll:
Header.js:
const Header = props => {
return (
<header className="header-container">
<div className="logo-container">
<img className="white-logo" src={logo} alt="Food Truck TrackR logo white" />
</div>
<section className="header-section-one">
<div className="location-sub-div">
<i class="fas fa-map-marker-alt"></i>
<h3>User Location</h3>
</div>
<div className="order-sub-div">
<i class="fas fa-store"></i>
<h3>Order now</h3>
</div>
</section>
<section className="header-section-two">
<NavLink to="/dine/search" className="search-sub-div">
<i class="fas fa-search search-icon"></i>
<h3>Search</h3>
</NavLink>
<div className="acct-sub-div">
<i class="fas fa-user acct-icon"></i>
<h3>Account</h3>
</div>
</section>
</header>
)
}
Header.scss:
.header-container {
width: 100%;
height: 9vh;
display: flex;
align-items: center;
color: black;
background: white;
font-size: 0.6rem;
padding-left: 4%;
padding-right: 4%;
position: fixed;
top: 0;
// border-bottom: 1px solid black;
z-index: 99;
}
.logo-container {
width: 20%;
margin-right: 6%;
.white-logo {
width: 100%;
}
}
.header-section-one {
width: 32%;
display: flex;
margin-right: 25%;
justify-content: space-evenly;
.location-sub-div {
display: flex;
h3 {
width: 100%;
white-space: nowrap;
}
}
.order-sub-div {
display: flex;
h3 {
white-space: nowrap;
}
}
}
.header-section-two {
width: 32%;
display: flex;
justify-content: space-evenly;
.search-sub-div {
display: flex;
.search-icon {
margin-right: 1%;
}
}
.acct-sub-div {
display: flex;
.acct-icon {
margin-right: 1%;
}
}
}
i {
margin-right: 1% !important;
}

This neat CSS trick might help you with the problem:
html:not([data-scroll='0']) {
.header-container {
width: 100%;
height: 9vh;
display: flex;
align-items: center;
color: black;
background: white;
font-size: 0.6rem;
padding-left: 4%;
padding-right: 4%;
position: fixed;
top: 0;
border-bottom: 1px solid black;
z-index: 99;
}
}
Here is a, link further explaining the solution: https://css-tricks.com/styling-based-on-scroll-position/
Good luck.

You can add an event listener to the document, and when a scroll event is fired, you can check to see what the vertical scroll position (scrollTop) of the document is, and conditionally show a border based on that value.
Here's an example:
import React, { useEffect, useState } from "react";
const Header = () => {
// Store a bool that determines if the border is visible
const [isBorderVisible, setIsBorderVisible] = useState(false);
useEffect(() => {
// Define a function that is called when the scroll event fires
const handleScroll = e => {
const scrollTop = e.target.documentElement.scrollTop;
if (scrollTop > 200) {
setIsBorderVisible(true);
} else {
setIsBorderVisible(false);
}
};
// Add the event listener inside a useEffect
if (document) {
document.addEventListener("scroll", handleScroll);
}
// Remove the event listener on unmount
return () => {
if (document) {
document.removeEventListener("scroll", handleScroll);
}
};
}, [setIsBorderVisible]);
return (
<div
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
height: "100px",
background: "hotpink",
// Conditionally style the border
borderBottom: isBorderVisible ? "2px solid #000" : "0"
}}
/>
);
};
So we have a useEffect, that adds an event listener to the document, listening for the scroll event. Whenever a scroll occurs, the handleScroll function (also defined inside the useEffect) fires.
In this function, we get the scrollTop value, which is the number of pixels that have been scrolled from the top of the document.
In the example, we are setting the state value isBorderVisible to true once we have a scrollTop greater than 200 pixels, but this can be anything you want.
In the header's style, we conditionally set a border, based on the state value of isBorderVisible.

Related

Centering a div with css in nextjs (moves but doesn't completely center)

I have tried looking at many questions on StackOverflow and none of them have helped me. I think that I have a more specific problem, there might be some type of CSS attribute that is stopping the div from centering. Maybe I need to use flexbox to solve this?
here is the JSX:
import { useState } from 'react'
import styles from '../styles/login.module.css'
import Head from 'next/head'
export default function Login() {
const [loginDetails, setLoginDetails] = useState({username: "", password: ""});
function handleUsernameChange(e) {
var value = e.target.value;
setLoginDetails({username: value, password: loginDetails.password});
}
function handlePasswordChange(e) {
var value = e.target.value;
setLoginDetails({username: loginDetails.username, password: value});
}
function SubmitLogin() {
console.log(loginDetails);
}
return (
<>
<Head>
<title>Login</title>
</Head>
<div className={styles.container}>
<h1 className={styles.subheading}>Login</h1>
<label>Username:</label>
<input type="text" placeholder="username" className={styles.input} onChange={handleUsernameChange} />
<label>Password:</label>
<input type="password" placeholder="password" className={styles.input} onChange={handlePasswordChange} />
<button className={styles.button} onClick={SubmitLogin}>Login</button>
</div>
</>
);
}
Here is the CSS:
.heading {
font-size: 3rem;
}
.subheading {
font-size: 2rem;
}
.pheading {
font-size: 1.5rem;
}
.paragraph {
margin-left: 1rem;
}
.container {
display: flex;
flex-direction: column;
gap: 10px;
margin: 0 auto;
width: 25%;
}
.input {
border-radius: 5px;
max-width: 200px;
height: 30px;
border: 1px solid #d7dbd9;
}
.input:focus {
outline: none;
border: 1px solid #67f0ab;
}
.button {
border: 2px solid #67f0ab;
border-radius: 5px;
color: #67f0ab;
background: none;
max-width: 100px;
height: 30px;
}
.button:hover {
background-color: #67f0ab;
color: #FFF;
}
.button:active {
background-color: #49de93;
}
The element I am trying to center is the div wrapping the login form. It has the class of .container, this class is what I was editing in my CSS to try and solve the problem. I can get the element to move to different spots, but I can't center it.
Ok so I figured out what the problem was, it was that the max-width of my container was larger than that of my widest element inside of the container, in this case it happened to be the inputs
I changed the max-width value of my container to 200px which is the same as my .input class, and the container and all of its elements were then centered.

How to fit React Modal size children?

I am trying to load a dynamic login page with React Modal. Is there a way to make React Modal resize to the size of its child elements?
//App.js
import "./styles/app.scss";
import Layout from './options/Layout'
import Login from './Components/Login'
import { useState } from "react";
import Modal from 'react-modal'
function App() {
const [OpenModal, setOpenModal] = useState(false)
return (
<div id="app" className="App">
<Layout>
<button onClick = {() => setOpenModal(true)}>open modal</button>
<Modal isOpen = {OpenModal}>
<Login/>
</Modal>
</Layout>
</div>
);
}
export default App;
//login.scss
.login {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 280px;
margin: 0 auto;
background-color: #5c8fc2;
border-radius: 30px;
}
.login_top {
display: flex;
align-items: center;
justify-content: center;
margin-top: -40px;
margin-bottom: 20px;
color: whitesmoke;
}
.login_register_container {
border: none;
outline: none;
border-radius: 50%;
}
button {
margin-right: 12px;
margin-top: 30px;
border: none;
border-radius: 4px;
cursor: pointer;
}
I want to dynamically manage the size according to the child element.
I've tried several methods, but without success. How to fit modal's component to child element???
If you use the developer tools you can see that the content grows because they have applied some styles to the modal.
position: absolute;
inset:40
inset is same as
top: 40px;
left: 40px;
right: 40px;
bottom: 40px;
If you want your content to be exactly the same size as your content, you can remove the right and bottom from the styles.
<ReactModal
isOpen={true}
contentLabel="Minimal Modal Example"
className="Modal"
>
<div>
<div style={{ height: '500px' }}>Some content</div>
</div>
<button onClick={() => {}}>Close Modal</button>
</ReactModal>
CSS:
.Modal {
position: absolute;
top: 40px;
left: 40px;
background-color: papayawhip;
}
You will lose equal spacing on all sides of the modal if you do this though.
Here is an example. https://stackblitz.com/edit/react-vxe3cn

How to adjust height of div based on content

I have designed a reusable ListItem component which I then map over in another component to create a List. The list looks good in an expanded window however when I shrink the window size the text overflows out of the div and onto subsequent items in the list. I am trying to understand how to make it so the height of the list item changes depending on the text inside.
I have searched for an answer and tried the following without success:
in #list-item set height:auto
in #list-item set height:fit-content
In displaying component apply a style of "display:block" to ListItem component during mapping.
Issue Screenshot
Result of solutions 1 and 2 above
No difference using solution 3
ListItem.js
export default function ListItem(props) {
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => setAnchorEl(event.currentTarget);
const handleClose = () => setAnchorEl(null);
return (
<div>
<div id='list-item'>
<div id="label-container">
<Typography id="type" variant="caption">{props.type}</Typography>
<p id="title">{props.title}</p>
</div>
<SvgIcon id="icon" onClick={handleClick}>
<ListIcon />
</SvgIcon>
</div>
<Divider id="divider" />
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>View / Edit</MenuItem>
<MenuItem onClick={handleClose}>Add to Community</MenuItem>
<MenuItem onClick={handleClose}>Share with...</MenuItem>
</Menu>
</div>
)
}
ListItem.css
#list-item {
display:flex;
width:70%;
min-height:54px;
align-items: center;
justify-content: center;
position: relative;
margin: 0 auto;
}
#label-container {
display:flex;
flex-direction: column;
justify-content: center;
}
#type {
position: absolute;
margin-right: 40px;
top: 5px;
left: 10px;
}
#title {
position: absolute;
margin-top: 0px;
margin-right: 40px;
top:25px;
left:10px;
}
#divider {
width:70%;
margin: 0 auto;
}
#no-margin {
margin:0;
}
#icon {
position: absolute;
fill: #70CDE5;
right: 10px;
}
#media screen and (max-width: 600px){
#list-item{
width:100%;
}
#divider {
width: 100%;
}
}
Displaying Component
...
{listData.map((item, index) => (
<ListItem key={item.title} type={item.type} title={item.title} />
))}
...
Thank you
It's probably because you have position: absolute on your Typography and p.
When set position: absolute to any element it removed it from the document flow, that's why your list height is the min-height: 54px only.
Try removing position: absolute
#type {
margin-right: 40px;
}
#title {
margin-top: 0px;
margin-right: 40px;
}

min-height pushes the content outside of div instead of growing inside div

I have created a simple to-do app. I am creating div for every to-do being created and set min-height for the div in case if the content grows larger than div but even after setting the min-height of div content grows outside of div. Could anyone please point out what I am missing? I have attached the screenshot of the output.
App.css
body {
background-color: rgb(238, 174, 174);
}
.App {
border: 1px solid;
border-radius: 20px;
min-height: 200px;
width: 30%;
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
}
Todo.css
.Todo {
border: 1px solid black;
margin: 10px;
width: 300px;
height: 50px;
min-height: 50px;
padding-left: 9px;
padding-bottom: 9px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Todo.js
import React from 'react';
import "./Todo.css"
const Todo = ({title, description}) => {
return (
<div className="Todo">
<p>{title}</p>
</div>
)
}
export default Todo
App.js
import React, {useState} from 'react';
import Todo from './Todo';
import './App.css';
function App() {
const [todos, setTodos] = useState([]) // todos => to store the value, setTodos => to update the state
const [input, setInput] = useState("")
const handleSubmit = (e) => {
e.preventDefault();
setTodos([...todos, input])
setInput("")
}
return (
<div className="App">
<h1>To-do-app</h1>
<form>
<input type="text" onChange = {(e) => setInput(e.target.value)} value={input} />
<button type="submit" onClick={handleSubmit}>Add Todo</button>
</form>
{
todos.map(todo => {
return <Todo title={todo} />
})
}
</div>
);
}
export default App;
Try this - lose the min-height change height: auto
.Todo {
border: 1px solid black;
margin: 10px;
width: 300px;
height: auto;
padding-left: 9px;
padding-bottom: 9px;
}
You need to remove the height from Todo.css(Todo class) and just keep the min-height
.Todo {
border: 1px solid black;
margin: 10px;
width: 300px;
min-height: 50px;
padding-left: 9px;
padding-bottom: 9px;
}

How to set the content of div to be visible inside parent div using css

i want the content inside the expandable_container div to be visible.
Now it moves up while adding align-items: center to the expandable div.
I have a side panel which contains list items. when the list item overflows i add a expand button. clicking that button should show the full content of list item. It works but then. when the content in list item overflows then the content in list item is half seen. like in below image
i want that content to properly placed. I tried adding padding-top to expandable class. However this will affect other list items that doesnt have expandable component. How can i fix this. Below is the code,
export default class Expandable extends React.PureComponent{
constructor(props) {
super(props);
this.expandable_ref = React.createRef();
this.state = {
expanded: false,
overflow: false,
};
}
componentDidMount () {
if (this.expandable_ref.current.offsetHeight <
this.expandable_ref.current.scrollHeight) {
this.setState({overflow: true});
}
}
on_expand = () => {
this.setState({expanded: true});
console.log("in expnad");
};
on_collapse = () => {
this.setState({expanded: false});
};
render () {
return (
<div className={(this.state.overflow ?
this.props.container_classname : '')}>
<div className={(this.state.overflow ?
this.props.classname : '')} style={{overflow: 'hidden',
display: 'flex', height: (this.state.expanded ? null :
this.props.base_height)}}
ref={this.expandable_ref}>
{this.props.children}
</div>
{this.state.overflow && this.state.expanded &&
<div className={this.props.expand}>
<button onClick={this.on_collapse}>
{this.props.arrow_up}</button>
</div>}
{this.state.overflow && !this.state.expanded &&
<div className={this.props.expand}>
<button onClick={this.on_expand}>
{this.props.arrow_down}</button>
</div>}
</div>
);
}
}
Class SidePanel extends React.purecomponent {
switch (notification.type) {
case 'new_model_uploaded':
return (
<Expandable
base_height={42}
arrow_down={<SvgAccDown className='grey' width="10"
height="10"/>}
arrow_up={<SvgAccUp className='grey' width="26"
height="26"/>}
container_classname='expandable_container'
classname='expandable'
expand='expand'>
<ListItem
icon={<SvgProject width="26" height="26"/>}
text={<Text
name={notification.initiator.name}
text=' created model '
model_name={notification.attributes.modelname}/>}
timestamp={notification.timestamp}>
<div className="additional_details">
<PreviewImage
width={70}
height={70}
model_id={filtered_model.id}
/>
</div>
</ListItem>
</Expandable>
);
case 'deleted':
return (
<ListItem
icon={<Svg width="20" height="22"/>}
text={<Text
name={notification.initiator.name}
text=' deleted model '
model_name={notification.attributes.modelname}/>}
timestamp={notification.timestamp}/>
);}
}
function ListItem(props) {
return (
<li className="notification">
<div className="details_container">
<div className="details">
{props.icon}
{props.text}
<Timestamp>{props.timestamp}</Timestamp>
</div>
{props.children}
</div>
</li>
);
}
.notification {
display: flex;
flex-direction: row;
font-size: 12px;
padding: 8px;
min-height: 49px;
flex-grow: 1;
li {
list-style: none;
}
.details_container {
display: flex;
flex-direction: column;
flex-grow: 1;
margin-right: 8px;
.details {
display: flex;
color: #333;
align-items: center;
flex-grow: 1;
svg {
margin-right: 8px;
margin-left: 7px;
flex: 0 0 auto;
align-self: center;
flex-shrink: 0;
}
span {
flex-grow: 5;
text-align: left;
}
time {
flex: 0 0 auto;
margin-left: 8px;
padding-top: 2px;
color: #CCCCCC;
}
}
.additional_details {
flex-basis: 100%;
width: 226px;
margin-left: 11%;
span {
display: block;
word-break: break-all;
margin-left: 2px;
}
}
}
}
.expandable_container {
display: flex;
margin-top: 8px;
flex-direction: column;
border-bottom: 1px solid #CCCCCC;
.expandable {
align-items: center;
padding-top: 35px;
}
}
.expand {
display: flex;
align-items: center;
position: relative;
top: 10px;
border: 1px solid #CCCCCC;
width: 150px;
height: 20px;
left: 30%;
background-color: $white;
justify-content: center;
}
Could someone help me solve this thanks.

Resources