Lets say in my global.css file of a Next.js project I have:
.flex {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
I also have a Layout.js component and a Layout.module.css file. The component looks like this:
import styles from "../styles/Layout.module.css";
const Layout = ({ children }) => {
return (
<div>
<div className={styles.navbar}>
<div className="flex">
<h1>Structured Safety</h1>
<nav>
<ul>
<li> Home </li>
<li> Demo </li>
</ul>
</nav>
</div>
</div>
</div>
);
};
export default Layout;
and the Layout.module.css is:
/* Navbar */
.navbar {
background-color: var(--primary-color);
color: #fff;
height: 70px;
}
.navbar ul {
display: flex;
}
.navbar .flex {
justify-content: space-between;
}
Structured like this, my .navbar .flex does not overwrite the global .flex class and split the h1 from the nav. How can I accomplish overwriting my global style from this component style?
Since .flex refers to a global class you'll need to use the :global selector to target it in your CSS module.
/* Layout.module.css */
.navbar :global(.flex) {
justify-content: space-between;
}
Or using an alternate syntax.
.navbar {
:global {
.flex {
justify-content: space-between;
}
}
}
/** CSS MODULE FILE **/
.classname :global(.globalClass) { css properties }
.classname {
:global {
.globalClass { css properties }
}
}
In NextJS and React when you
import styles from "__.css" the styles becomes a variable so you have to use it in your HTML for it to take effect.
Currently you're not using any styles from your Layout.module.css, if you want to use that css you would change your html to: <div className={styles.navbar}> and such..
Related
I'm trying to create a custom component which can take other HTML Elements, however, it has to be flexible by not having to use <template> and slots templates in the HTML file every time the component is used. Already tried querying after every render by using const children = this.shadow.querySelectorAll('.menu > *') within the connectedCallback() function, but the result is an empty NodeList
class SideMenu extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({mode:'open'});
}
render() {
this.shadow.innerHTML = `
<style>
.menu {
display: flex;
flex-direction: column;
height: 100vh;
padding: 2em;
width: 33vw;
gap: 10px;
background-color: #2C2B2B;
}
</style>
<div class="menu">
</div>
`;
}
connectedCallback() {
this.render();
}
}
customElements.define('side-menu', SideMenu);
<side-menu>
<div>Element1</div>
<div>Element2</div>
<div>Element3</div>
<div>ElementN</div>
</side-menu>
Basically, the custom element has to be able to have any number of elements within, like an unordered list (<ul>) which can have multiple <li> inside.
You have a shadowDOM, so don't need an extra div when you style the component with :host
you can manually move ligthDOM elements to shadowDOM
super() sets AND returns the 'this' scope
attachShadow sets AND returns this.shadowRoot
append wasn't available in Internet Explorer, ("oldfashioned" devs still use appendChild)
note it moves the DOM elements from lightDOM to shadowDOM. And if you move Web Components, the connectedCallback will run again (but lightDOM will then be empty) So be aware what you do in the connectedCallback
customElements.define('side-menu', class extends HTMLElement {
constructor() {
super().attachShadow({mode:'open'}).innerHTML = `
<style>
:host {
display: flex; flex-direction: column; gap: 10px;
height: 100vh; width: 33vw;
padding: 2em;
background-color: #2C2B2B; color: gold;
}
</style>`;
}
connectedCallback() {
this.shadowRoot.append(...this.children);
}
});
<side-menu>
<div>Element1</div>
<div>Element2</div>
<div>Element3</div>
<div>ElementN</div>
</side-menu>
I am creating a basic nav bar and I want to change it based on screen size. Once it hits 600px i'd like to hide the links and display a clickable nav button that will expand those options.
After console logging my list Elements I found that the className was given this 'Nav_floatLeft__H1YZ8'. So based on that finding, my code is as follows. However, my navigation does not display any changes when clicking the button.
I'm sure React has a better way of handling this situation, but I'm fairly new to it. Should I be using some kind of state/effect hook?
Nav:
const openCloseMenu = () => {
console.log(document.getElementsByClassName(styles.floatLeft).className);
let elements = document.getElementsByClassName(styles.floatLeft);
if (elements.className === "Nav_floatLeft__H1YZ8"){
alert("Changed to: Menu Bar Expanded");
elements.className = styles.menuBarExpanded;
}
else {
alert("Changed Back to: Float Left")
elements.className = styles.floatLeft;
}
}
return (
<div className={styles.topNav}>
<nav>
<ul className={styles.inlineListItem}>
<li className={styles.floatLeft}>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li className={styles.floatLeft}>
<Link href="/search">
<a>Search</a>
</Link>
</li>
<li className={styles.menuBar}>
<button onClick={openCloseMenu}>Expand Nav</button>
</li>
</ul>
</nav>
</div>
)
CSS:
.inlineListItem{
display: inline;
list-style: none;
}
.floatLeft{
float: left;
margin: 1rem;
padding-left: 1rem;
text-align: center;
color: white;
}
.floatRight{
display: inline;
float: right;
color: white;
margin: 1rem;
padding-right: 2rem;
}
.menuBar{
display: none;
float: left;
margin: 1rem;
padding-left: 1rem;
text-align: center;
color: white;
}
.menuBarExpanded{
display: block;
}
#media screen and (max-width: 600px) {
.menuBar{
display: block;
}
.floatLeft{
display: none;
}
.floatRight{
display: none;
}
}
ways are there to solve this problem.
You can use material UI. To detect the breakpoint.
Like for you case. If want to detect 600px.
you can do something like that ---
const themeBreakpoint = theme.breakpoint.down('600px) // themeBreakpoint will be true under 600px.
using this flag you can change state and show what ever you want by using condition rendering.
Second Problem --
you can change any state based on onClick event.
like --
const[clicked,setclicked] = useState(false)
const handleClick = (e) =>{
setclicked(true)
}
now when you make that clicked flag true, You change you css class based on that flag.
You could implement an useState hook:
import { useState } from "react";
const YourComponent = () => {
const [cliked, setClicked] = useState(false);
return (
<YourNavbar className={clicked ? "display" : "hide"} />
....
<button onClick={() => setClicked(current => !current)}>Expand Nav</button>
....
)
}
And on css, you can establish the class display with the actual attributes of the navbar, and a hidden (display: none)
This will check if the navbar button has been clicked (set to true), and on the conditional class, if its true, then it will display the navbar through the "display" class, if the button is clicked again, clicked will be false and the class for the navbar will be "hidden".
Remember to delegate this classnames to the item only when the navbar is below 600px with #media
I wrote a Web Component using Vue.js and vue-custom-element. Now I want to make my-chat and my-whiteboard Web Components "height:100%".I'm using the component like this:
// App.vue
<template>
<splitpanes class="default-theme">
<pane>
<my-chat></my-chat>
</pane>
<pane>
<my-whiteboard></my-whiteboard>
</pane>
</splitpanes>
</template>
The only way that I know is to set the height of all parents to 100% like this:
html,
body,
splitpanes,
pane,
my-chat,
my-whiteboard {
height: 100%;
}
//main.js
...
// Load custom element plugin
Vue.use(vueCustomElement);
// Define web components
Vue.customElement("skyroom-whiteboard", Whiteboard);
Vue.customElement("skyroom-chat", Chat);
...
And do this for the all tags inside web my-chat and my-whiteboard too!!!
The problems:
This is not working for my- components.
It seems to be wrong! Isn't there any right way to do this?
The simple way to do it is to use
my-chat, my-whiteboard {
min-height: 100vh;
}
However, when one of them becomes taller than 100vh, it will grow without the other one. So, most likely, ...
display: block;
height: 100vh;
overflow-y: auto;
... will do a better job.
Here's an example (you don't need any the CSS after /* let's test it */ line but I had to add it as all of them are custom elements and, by default, they have a display value of inline):
my-chat,
my-whiteboard {
display: block;
height: 100vh;
overflow-y: auto;
}
/* let's check it */
my-chat,
my-whiteboard {
box-sizing: border-box;
}
tester {
height: 200vh;
padding: 1rem;
}
splitpanes { display: flex; }
pane:first-child { flex-basis: 75%; }
pane:last-child { flex-grow: 1; }
body { margin: 0; }
/* don't use this line in your app, it will likely break stuff
* I used it here because I don't have any content to break! */
.default-theme * { display: block; }
<splitpanes class="default-theme">
<pane>
<my-chat>
<tester>my-chat</tester>
</my-chat>
</pane>
<pane>
<my-whiteboard>
<tester>my-whiteboard</tester>
</my-whiteboard>
</pane>
</splitpanes>
Important note: If any of your components gets parsed by Vue into actual <div> elements, you'll need to change the selectors accordingly (but you did say they're custom elements, so I'm guessing they're used as-is).
I'm following the React tutorial and have a question about their starter code:
class Square extends React.Component {
render() {
return (
<button className="square">
{/* TODO */}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square />;
}
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
class Game extends React.Component {
render() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);
with the following CSS, in which I've commented out the .board-row:after directives:
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
}
ol, ul {
padding-left: 30px;
}
/* .board-row:after {
clear: both;
content: "";
display: table;
} */
.status {
margin-bottom: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
.square:focus {
outline: none;
}
.kbd-navigation .square:focus {
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
What I don't understand is after commenting out these directives,
.board-row:after {
clear: both;
content: "";
display: table;
}
which appear to be an implementation of the "Easy Clearing Method" described in All About Floats, I still see the squares organized in an 'array' as they originally were, and not all in a row. Since the default display property is inline, I would expect all 9 squares to displayed in a single row without the 'easy clearing method'. Why does the appearance not change?
tl;dr use Flexbox
You seem to have misunderstandings in a lot of different places.
This problem isn't specific to React, it's a problem with the HTML and CSS you're using. You're referring to .board-row:after as a "directive", but it's really just a simple CSS rule
Different HTML elements have different default CSS property values. You're using div elements, which have a default display value of block, which means that it will drop to the next line, instead of appearing "inline" with its siblings
A couple years ago, the only way to achieve what you're trying to do with elements that are set to display: block was to use floats. Applying float: left to a series of divs would cause them to appear next to eachother on the same line. Floats, however, can be painful to work with; one pain point is that in order for a parent HTML element to be aware of elements inside it that use floats, you needed to "clear" your floats by adding an element at the end with clear: both. That's where the clearfix trick came into play. Nowadays, however, Flexbox is the way to achieve what you're trying to do.
I have a navigation bar for my webapp with the following css setup:
.navigation {
background: white;
display: flex;
height: 5em;
align-items: center;
padding: 0px 2em;
color: blue;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.075em;
border-bottom: 1px solid #E6E6E6;}
My issue is on mobile, my tabs in the navigation bar get all squeezed together. Is there a way in React that I can detect the width of the page and collapse all my navigation tabs into a dropdown menu? Or is this a CSS thing?
You could handle this in CSS, using proper media queries.
But if you prefer to do it with React, here is how you can implement it, listening to the window "resize" event:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
layoutMode: this.getLayoutMode(),
};
this.onResize = this.onResize.bind(this);
}
componentDidMount() {
window.addEventListener('resize', this.onResize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.onResize);
}
onResize() {
this.setState({
layoutMode: this.getLayoutMode(),
});
}
getLayoutMode() {
return window.innerWidth > 1000 ?
'desktop'
: 'mobile';
}
render() {
return this.state.layoutMode === 'desktop' ? (
<NavigationBar />
) : (
<DropdownMenu />
);
}
}
Seriously, your best option is css. Let react focus on the structure of your DOM and on the interaction. Let css take care of styling.
So you can keep your react code simple like this:
render() {
var myMenu = ['Home','Settings','About us', 'Other stuff'];
return (
<div>
<button className='hamburger'>m</button>
<ul className='menu'>
{myMenu.map(item => {
return <MenuItem key={item} text={item}/>
})}
</ul>
</div>
);
}
And do the styling stuff in css (code below with Sass):
ul.menu
position: absolute
display: flex
flex-direction: row
width: 100%
li.menu-item
flex: 1 0 auto
padding: 10px
margin: 2px
background-color: grey
text-align: center
li.menu-item:hover
background-color: blue
button.hamburger
display: none
padding: 10px
margin: 2px
background-color: grey
#media screen and (max-width : 760px)
ul.menu
flex-direction: column
display: none
li.menu-item
background-color: orange
button.hamburger
display: block
position: absolute
button.hamburger:focus + ul.menu
display: flex
You can find a working demo in codepen here.
If you want to render a drop-down menu on mobile, here's one strategy:
Listen for media query changes in React: http://krasimirtsonev.com/blog/article/Using-media-queries-in-JavaScript-AbsurdJS-edition.
Use that listener to update this.state on the component (ex: this.state.isMobile).
Render different Navigation components based on the media query conditional (ex: this.state.isMobile ? <Navigation type="mobile" /> : <Navigation type="desktop" />).