I'm trying to make a portfolio site using gatsbyjs and I'm currently having problems creating project cards using graphql and gatsby image sharp. I'm not sure if it's just my css or my js.
Basically I have a projects page that is getting data from a local JSON file and then passing the data to a card component which is then rendered on the projects page. I have css grid for the card container but the problem is the cards component doesn't seem to recognize the grid and just spills out or they just goes down/across the page. Kinda hard to explain. Still learning aha. I just want 3 cards on top and then 3 on the bottom.
https://github.com/verv0022/portfolio
they seem to take up the entire grid space even though i have a width?
Project Page
const Projects = () => {
const data = useStaticQuery(graphql`
query ProjectsQuery {
allProjectsJson {
edges {
node {
id
name
description
url
image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
`)
const projects = data.allProjectsJson.edges
console.log(projects)
return (
<section className="projects">
<main className="projects-content">
<ScrollAnimation
className="projects-title"
animateOnce={true}
animateIn="slideInLeft"
animatePreScroll={false}
initiallyVisible={false}
>
<h2>Here are some of my projects...</h2>
</ScrollAnimation>
<section className="project-preview-container">
<div className="project-preview-item-container">
{projects.map(({ node: project }) => {
return (
<ProjectPreviewItem
key={project.id}
name={project.name}
description={project.description}
imageData={project.image.childImageSharp.fluid}
url={project.url}
/>
)
})}
</div>
</section>
</main>
</section>
)
}
export default Projects
Card Component
import React from "react"
import Image from "gatsby-image"
import "./Projects.css"
const ProjectPreviewItem = ({ name, imageData, description, url }) => {
return (
<div className="project-item">
<div className="image-container">
<Image fluid={imageData} alt={name} className="project-img"></Image>
</div>
<div className="project-item-details">
<h2 className="project-url">
<a href={url}>{name}</a>
</h2>
<p className="project-description">{description}</p>
</div>
</div>
)
}
export default ProjectPreviewItem
CSS
body,
html {
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
.projects {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(6, 1fr);
grid-column-gap: 0px;
grid-row-gap: 0px;
display: grid;
height: 100vh;
}
.projects-content {
grid-area: 2 / 2 / 6 / 6;
}
.projects-title {
font-size: 26px;
}
.project-preview-container {
grid-area: 3 / 2 / 6 / 6;
margin-top: 6rem;
background-color: lightgray;
}
/* .project-preview-item-container {
} */
.projects-title {
width: 40rem;
}
.project-item {
display: flex;
flex-direction: column;
justify-content: center;
border: black solid 2px;
padding: 1rem;
margin: 1rem;
width: 250px;
height: auto;
}
.project-item-details {
margin-top: 20px;
}
#keyframes slideInLeft {
from {
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
visibility: visible;
}
to {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
Your markup looks something like this currently.
.projects
.projects-content
.project-preview-container
.project-item
.project-item
.project-item
But should be something like.
.projects
.project-item
.project-item
.project-item
Grid items must be children of the grid.
.projects {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
background-color: lightgray;
}
.project-item {
border: 1px solid black;
height: 80px;
background: white;
}
<div class="projects">
<div class="project-item">One</div>
<div class="project-item">Two</div>
<div class="project-item">Three</div>
<div class="project-item">Four</div>
<div class="project-item">Five</div>
<div class="project-item">Six</div>
</div>
Have a read of the Basic Concepts of Grid Layout
Related
I am running into an issue with getting SimpleLightbox not loading for me until I refresh the page on my Gatsby site. I have a demo up where if you go from the "inspirations" page (https://effulgent-liger-a1d01e.netlify.app/inspiration) then click on an individual inspiration you will see by clicking on the photo in the gallery that it just opens the photo url and not the lightbox. Then if you refresh the page and click an image in the gallery it then loads the lightbox fine. I assume this is part of my Gatsby learning process, but stumped on this. Thanks for any help.
Here is my individual "inspiration" post file code.
import React from 'react'
import styled from 'styled-components'
import { GatsbyImage } from 'gatsby-plugin-image'
import SimpleLightbox from 'simple-lightbox'
import 'simple-lightbox/dist/simpleLightbox.min.css'
import Seo from "../components/seo"
import CTAOne from '../components/ctaOne'
const HeroSection = styled.section`
height: 650px;
position: relative;
#media(min-width: 768px) {
height: 825px;
}
.hero-image .gatsby-image-wrapper {
width: 100%;
height: 650px;
object-fit: cover;
object-position: center;
#media(min-width: 768px) {
height: 825px;
}
}
.hero-content {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
z-index: 1;
text-align: center;
width: calc(100% - 20px);
max-width: 1160px;
padding: 15px 0;
background-color: var(--white-trans);
.hero-border-top,
.hero-border-bottom {
height: 18px;
width: 100%;
border: 6px solid var(--white-trans);
}
.hero-border-top {
margin-top: -33px;
}
.hero-border-bottom {
margin-bottom: -33px;
}
h1 {
margin-bottom: 0;
}
p {
font-size: 21px;
line-height: 1.2;
width: 100%;
max-width: 900px;
margin: 25px auto 21px auto;
padding-left: 10px;
padding-right: 10px;
#media(min-width: 768px) {
font-size: 23px;
}
}
}
`
const InspirationPostContainer = styled.section`
margin-bottom: 90px;
.inspiration-desc {
max-width: 850px;
margin: 90px auto 85px auto;
text-align: center;
}
.inspiration-grid {
column-count: 1;
column-gap: 55px;
#media(min-width: 640px) {
column-count: 2;
}
#media(min-width: 992px) {
column-count: 3;
}
}
.item {
margin: 0 0 40px;
display: grid;
grid-template-rows: 1fr auto;
break-inside: avoid;
}
.item > img {
grid-row: 1 / -1;
grid-column: 1;
}
`
function InspirationPost({ pageContext }) {
const { inspiration } = pageContext
if (typeof window !== `undefined`) {
new SimpleLightbox({elements: '.inspiration-grid a'})
}
return (
<>
<HeroSection>
<div className='hero-image'>
<GatsbyImage image={inspiration.inspiration.projectFeaturedPhoto.gatsbyImage} alt={inspiration.inspiration.projectFeaturedPhoto.altText} />
</div>
<div className="hero-content">
<div className='hero-border-top'></div>
<h1>{inspiration.inspiration.projectTitle}</h1>
<div className='hero-border-bottom'></div>
</div>
</HeroSection>
<InspirationPostContainer>
<div className="container">
<div className='inspiration-desc'>{inspiration.inspiration.projectDescription}</div>
<h2>Project Gallery</h2>
<div className='inspiration-grid'>
{inspiration.inspiration.projectGallery.map((node) => (
<a href={`${node.mediaItemUrl}`} className='item' key={node.id}>
<GatsbyImage image={node.gatsbyImage} alt={node.altText} />
</a>
))}
</div>
</div>
</InspirationPostContainer>
<CTAOne />
</>
)
}
export default InspirationPost
The issue seems to be related to the SimpleLightbox module. Have you tried something like this?
useEffect(()=>{
if (typeof window !== `undefined`) {
new SimpleLightbox({elements: '.inspiration-grid a'})
}
},[])
It looks like the library is not loaded at the first initial render or the elements are not ready at the same time that the lightbox is requesting them, but when hydration occurs, everything works as it should, so loading it at the right time or forcing a reload should work.
I am working on a WordPress site and I have a snippet of html that iterates with repeating classes.
I am attempting to create a click function but only affect the element that is clicked. All in JavaScript.
As of right now my function is affecting all elements with the class name. Test code can be found at my CodePen or below.
I can accomplish this without nested loops as seen here. So my assumption is the problem lies within the second forEach loop. I would appreciate any light on the matter.
Thank you in advance.
/**
*Constructors
**/
const carousel = document.getElementsByClassName("carousel");
const btns = document.getElementsByClassName("btns");
/**
*Execute
**/
Array.from(btns).forEach((i) => {
i.addEventListener("click", (e) => {
Array.from(carousel).forEach((n) => {
if (i.classList.contains("slide-left")) {
n.scrollLeft -= 20;
} else if (i.classList.contains("slide-right")) {
n.scrollLeft += 20;
} else {
alert("ut oh");
}
});
});
});
/*
**Utilities
*/
/*containers*/
.feed-container {
position: absolute;
height: 200px;
width: 100%;
display: grid;
grid-template-columns: 1;
grid-template-rows: 1;
}
.carousel {
grid-row: 1;
grid-column: 1/5;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 1;
grid-gap: 15px;
align-self: center;
border: 1px solid #ccc;
overflow-x: scroll;
overflow-y: hidden;
}
/*div-buttons*/
div[class*="slide-"] {
/*opacity: 0;*/
position: sticky;
grid-row: 1;
z-index: 5;
place-self: center;
transition: 0.5s;
padding: 15px;
}
.slide-left {
grid-column: 1;
}
.slide-right {
grid-column: 4;
}
/*items*/
div[class*="item-"] {
grid-row: 1;
width: 400px;
height: 200px;
}
.item-1 {
background: blue;
}
.item-2 {
background: red;
}
.item-3 {
background: grey;
}
.item-4 {
background: yellow;
}
/*scrollbar*/
::-webkit-scrollbar {
display: none;
}
/*chevrons*/
[class*="chevron-"] {
box-sizing: border-box;
position: relative;
display: block;
transform: scale(var(--ggs, 1));
width: 22px;
height: 22px;
border: 2px solid transparent;
border-radius: 25px;
}
[class*="chevron-"]::after {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
width: 40px;
height: 40px;
border-bottom: 8px solid;
border-left: 8px solid;
bottom: 0;
}
.chevron-left::after {
transform: rotate(45deg);
left: 15px;
}
.chevron-right::after {
transform: rotate(-135deg);
right: 15px;
}
/*
**Exceptions
*/
.btns:hover {
cursor: pointer;
}
.opaque {
opacity: 1 !important;
}
.show {
display: block;
}
<div id="wrapper" style="display:grid; grid-template-rows:repeat(2, auto); grid-gap: 100px;">
<div>
<h1>Header</h1>
<div class="feed-container">
<div class="carousel">
<div class="item-1"></div>
<div class="item-2"></div>
<div class="item-3"></div>
<div class="item-4"></div>
</div>
<div class="slide-left btns">
<div class="chevron-left"></div>
</div>
<div class="slide-right btns">
<div class="chevron-right"></div>
</div>
</div>
</div>
<br>
<div>
<h1>Header</h1>
<div class="feed-container">
<div class="carousel">
<div class="item-1"></div>
<div class="item-2"></div>
<div class="item-3"></div>
<div class="item-4"></div>
</div>
<div class="slide-left btns">
<div class="chevron-left"></div>
</div>
<div class="slide-right btns">
<div class="chevron-right"></div>
</div>
</div>
</div>
</div>
It's because you're getting all the elements with class name carousel and then looping through them with each click.
const carousel = document.getElementsByClassName("carousel");
Instead what you need to do is get the carousels only under the button's parent when you trigger the click event
eg something like this:
Array.from(btns).forEach((i) => {
i.addEventListener("click", (e) => {
const targetElement = e?.target || e?.srcElement;
const parent = targetElement.parentElement();
const carousel = Array.from(parent.getElementsByClassName("carousel"));
carousel.forEach((n) => {
if (i.classList.contains("slide-left")) {
n.scrollLeft -= 20;
} else if (i.classList.contains("slide-right")) {
n.scrollLeft += 20;
} else {
alert("ut oh");
}
});
});
});
I took a look at the following as recommend and it seems to do the trick.
I created a variable that calls the parentNode "forEach()" button clicked. Oppose to looping through each element.
Working example, codePen
const carousel = document.querySelectorAll(".carousel");
const btns = document.querySelectorAll(".btns");
btns.forEach((i) => {
i.addEventListener("click", () => {
var x = i.parentNode;
var y = Array.from(x.querySelectorAll(".carousel"));
y.forEach((n) => {
if (i.classList.contains("slide-left")) {
n.scrollLeft -= 20;
} else {
n.scrollLeft += 20;
}
});
});
});
I'm trying to get a div to render until the bottom of the screen/page (and no further than that).
I'm able to do that successfully as long as this div is the only thing in the entire page. However, it's overflowing the page whenever I have anything above it (in this example, the text Some Header).
https://codesandbox.io/s/keen-bird-phx1f?file=/src/App.js
The React code:
import "./styles.css";
import React, { Fragment } from "react";
export default function App(props) {
return (
<Fragment>
<div>Some Header</div>
<div className="test1">
<div className="dashbar">Dash</div>
<div className="other-content">Other</div>
</div>
</Fragment>
);
}
The CSS code:
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.test1 {
display: flex;
height: 100vh;
border: 10px solid green;
}
.dashbar {
flex: 1 0 10%;
background: dodgerblue;
}
.other-content {
flex: 1 0 90%;
background: papayawhip;
}
How can I make it so that this div will stop at the bottom of the page and not overflow like this?
If I remove <div>Some Header</div>, then it behaves as expected:
.test1 has a height of 100vh, meaning you are forcing it to be exactly as tall as the viewport. But it has a header above it! So, the header necessarily pushes it down and it overflows the screen. You need a bit more of Flexbox trickery to get it right, especially flex-grow:1.
* {
box-sizing: border-box;
}
body {
margin: 0;
}
#root {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.test1 {
display: flex;
flex-grow: 1;
border: 10px solid green;
}
.dashbar {
flex: 1 0 10%;
background: dodgerblue;
}
.other-content {
flex: 1 0 90%;
background: papayawhip;
}
<body>
<div id="root">
<div>
Some Header<br>
with a couple<br>
of lines in it
</div>
<div class="test1">
<div class="dashbar">Dash</div>
<div class="other-content">Other</div>
</div>
</div>
</body>
It's because "test1" has height as 100vh and there is a static header text which has some height. Try this to fix:
* {
box-sizing: border-box;
line-height: 15px;
}
.test1 {
display: flex;
height: calc(100vh - 15px);
border: 10px solid green;
}
--- Dynamic solution:
https://codesandbox.io/s/stupefied-agnesi-x496f?file=/src/App.js
Basically I have two divs setup up and im trying to use flex box to easily size them so they both equally take up half the screen vertically using react. For some reason they are turning out looking like this.
Iv'e tried reinstalling npm but that didn't work at all, and i'm pretty sure my syntax is just fine.
This is my App.css
.wrapper,html,body {
height: 100%;
margin: 0;
}
.wrapper {
display: flex;
flex-direction: column;
}
.nav {
background-color: red;
flex: 1;
}
.main {
background-color: blue;
flex: 1;
}
This is my App.js
import {Row, Col } from "react-bootstrap";
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import './App.css'
class App extends Component {
render() {
return (
<div className = 'wrapper'>
<div className = 'nav'>
<text>hey</text>
</div>
<div className = 'main'>
<text>hey</text>
</div>
</div>
);
}
}
export default App;
They should work if you remove flex-direction: column; - you actually want flex-direction: row;, which is the default.
You're missing the flex: 1 prop inside the .wrapper css definition. The definition should look like this:
.wrapper {
display: flex;
flex:1;
flex-direction: column;
}
With flex: 1 you're telling the wrapper to use all the available space. Otherwise, it just use what it needs no matter the height.
Here's the entire CSS:
.wrapper,
html,
body {
height: 100%;
margin: 0;
}
.wrapper {
display: flex;
flex: 1;
flex-direction: column;
}
.nav {
background-color: red;
flex: 1;
}
.main {
background-color: blue;
flex: 1;
}
See the result:
Anyway, I totally recommend using Bootstrap v4 which is flexbox based by default.
Your code is all fine, just need add flex:1 to wrapper class.
you can read more about flex here : https://css-tricks.com/snippets/css/a-guide-to-flexbox/
.wrapper,
html,
body {
height: 100%;
margin: 0;
}
.wrapper {
display: flex;
flex-direction: column;
flex: 1;
}
.nav {
background-color: red;
flex: 1;
}
.main {
background-color: blue;
flex: 1;
}
<div class='wrapper'>
<div class='nav'>
<text>hey</text>
</div>
<div class='main'>
<text>hey</text>
</div>
</div>
I am trying to keep a seo friendly and semantic structure for my DOM, without repeating whole elements to display them in various positions.
My layout is based on display: flex items. I try to achieve the following:
Important things to know:
I do not want to show/hide divs based on the window width (to avoid unnecessary duplicates)
None of the divs has a known or fixed height
On desktops the divs should be vertical centered, while the right column builds a tag-team (behaves like one single div)
The layout needs to support at least IE11+
Is there a css only solution to achieve this?
If not, it would be easy to cut out the green div and paste its content into the pink one using javascript. But I do have concerns about the performance and "flickering" using this, although resizing the browser makes it more complicated. Do I make this needlessly complicated?
Here is fiddle showing a working solution but with javascript:
CODEPEN DEMO
In general, you can't do this with Flexbox alone, though there might be a compromise based on each given case.
With Flexbox alone, using fixed height, you can accomplish this
* {
box-sizing: border-box;
}
body, html {
margin: 0;
}
.flex {
width: 90%;
margin: 5vh auto;
height: 90vh;
background: rgba(0, 0, 0, 0.05);
display: flex;
flex-flow: column wrap;
}
.flex div {
flex: 1;
width: 50%;
}
.flex div:nth-child(2) {
order: -1;
}
.flex::before {
content: '';
height: 100%;
}
#media (max-width:768px) {
.flex div {
width: auto;
}
.flex::before {
display: none;
}
.flex div:nth-child(2) {
order: 0;
}
}
/* styling */
.flex-child {
color: white;
font-size: 2em;
font-weight: bold;
}
.flex-child:nth-child(1) {
background: #e6007e;
}
.flex-child:nth-child(2) {
background: #f4997c;
}
.flex-child:nth-child(3) {
background: #86c06b;
}
<div class="flex">
<div class="flex-child">
<div>Top/Right</div>
</div>
<div class="flex-child">
<div>Center/Left</div>
</div>
<div class="flex-child">
<div>Bottom/Right</div>
</div>
</div>
In this case, where no fixed height is allowed, you can combine Flexbox and float.
By set up it for mobile using Flexbox where you add the center item first in the markup and then, with order, move it between the top and bottom.
With a media query you then simply make the flex container a block element and use float to position the left to the left and the right to the right.
* {
box-sizing: border-box;
}
body, html {
margin: 0;
}
.flex {
max-width: 1024px;
width: 90%;
margin: 5vh auto;
height: 90vh;
background: rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
}
.flex-child {
color: white;
font-size: 2em;
font-weight: bold;
padding: 5%;
flex-basis: 33.333%;
display: flex;
align-items: center;
}
.flex-child:nth-child(1) {
background: #e6007e;
order: 1;
}
.flex-child:nth-child(2) {
background: #f4997c;
}
.flex-child:nth-child(3) {
background: #86c06b;
order: 2;
}
#media (min-width: 768px) {
.flex {
display: block;
}
.flex-child {
width: 50%;
}
.flex-child:nth-child(1) {
float: left;
height: 100%;
}
.flex-child:nth-child(2),
.flex-child:nth-child(3) {
float: right;
height: 50%;
}
}
<div class="flex">
<div class="flex-child">
<div>Center/Left</div>
</div>
<div class="flex-child">
<div>Top/Right</div>
</div>
<div class="flex-child">
<div>Bottom/Right</div>
</div>
</div>
Update
Here is another version combining Flexbox with position: absolute, which also vertically center the items in desktop mode
Updated, added a script to control so the absolute positioned element won't get bigger than the right items, and if so, adjust the flex containers height.
Note, the script is by no means optimized, it is only there to show how a fix in certain situations
(function() {
window.addEventListener("resize", resizeThrottler, false);
var fp = document.querySelector('.flex');
var fi = fp.querySelector('.flex-child:nth-child(1)');
var resizeTimeout;
function resizeThrottler() {
// ignore resize events as long as an actualResizeHandler execution is in the queue
if ( !resizeTimeout ) {
resizeTimeout = setTimeout(function() {
resizeTimeout = null;
actualResizeHandler();
// The actualResizeHandler will execute at a rate of 15fps
}, 66);
}
}
function actualResizeHandler() {
// handle the resize event
if (fp.offsetHeight <= fi.offsetHeight) {
fp.style.cssText = 'height: '+fi.offsetHeight+'px';
} else {
fp.style.cssText = 'height: auto';
}
}
window.addEventListener('load', function() {
actualResizeHandler();
})
}());
* {
box-sizing: border-box;
}
body, html {
margin: 0;
}
.flex {
position: relative;
max-width: 1024px;
width: 90%;
margin: 5vh auto;
height: 90vh;
background: rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
}
.flex-child {
color: white;
font-size: 2em;
font-weight: bold;
padding: 5%;
}
.flex-child:nth-child(1) {
order: 1;
}
.flex-child:nth-child(3) {
order: 2;
}
.flex-child:nth-child(1) div {
background: #e6007e;
}
.flex-child:nth-child(2) div {
background: #f4997c;
}
.flex-child:nth-child(3) div {
background: #86c06b;
}
#media (min-width: 768px) {
.flex {
justify-content: center;
}
.flex-child {
width: 50%;
}
.flex-child:nth-child(1) {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.flex-child:nth-child(n+2) {
margin-left: 50%;
}
}
<div class="flex">
<div class="flex-child">
<div>Center/Left<br>with more<br>content<br>than any<br>of the<br>other items<br>other items<br>other items<br>other items<br>other items</div>
</div>
<div class="flex-child">
<div>Top/Right<br>with more<br>content</div>
</div>
<div class="flex-child">
<div>Bottom/Right<br>with more</div>
</div>
</div>
With script one can also reorder/move items between elements.
Stack snippet
You can also combine this with a media query, and use it to do the actual re-order of the elements
$( document ).ready(function() {
$(window).resize(function() {
if ($( window ).width() < 600 ) {
$(".one").insertBefore("#b");
} else {
$(".one").insertBefore(".two");
}
});
});
.outer, #flex, #flex2 {
display: flex;
flex-direction: column;
}
#a {
order: 4;
background: #ccc;
}
#b {
order: 1;
background: #aaa;
}
#c {
order: 3;
background: #d33;
}
.one {
order: 2;
background: #aaa;
}
.two {
order: 5;
background: #aaa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer">
<div id="flex">
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
</div>
<div id="flex2">
<div class="one">Show me 2nd</div>
<div class="two">Show me 5th</div>
</div>
</div>
Update 2 (answered at another question but later moved here)
If we talk about smaller items, like a header or smaller menus, one can do what many website platform providers like "squarespace", "weebly", "wordpress", etc does. Their templates holds different markup structures, where an item sometimes exist twice, one visible for desktop, another for mobile.
Also, being so small, there will be less to nothing when it comes to performance (and personally I don't see anymore issue with this than having duplicate CSS rules, one for each screen size, and happily do this instead of introducing script).
Fiddle demo
Stack snippet
.container {
display: flex;
}
.container > div {
width: 50%;
}
.container div:nth-child(-n+2) {
border: dashed;
padding: 10px;
}
.container > div:nth-child(1) {
display: none; /* hide outer "Flower" */
}
#media (max-width:768px) {
.container {
flex-direction: column;
}
.container div {
width: auto;
}
.container div:nth-child(1) {
display: block; /* show outer "Flower" */
}
.container div:nth-child(3) div:nth-child(1) {
display: none; /* hide inner "Flower" */
}
}
<div class="container">
<div>Flower</div>
<div>Tree</div>
<div>
<div>Flower</div>
<div>Bee</div>
</div>
</div>