How to use Media Queries inside a React Styled Components Keyframe? - reactjs

I can get media queries to work properly inside a regular styled-components component however when I attempted to use it in a keyframe (via import from styled-components) it does not seem to work at all.
Attempting to get a div to animate to a specific position, but have that end position change when the window is < 800px, I've attempted this:
import styled, { keyframes } from 'styled-components';
// ... further down ...
const slideAnim = keyframes`
100% {
top: 20px;
left: 30px;
}
#media (max-width: 800px) {
top: 70px;
left: 50px;
}
`;
I have also tried putting the media query in the 100% block:
const slideAnim = keyframes`
100% {
top: 20px;
left: 30px;
#media (max-width: 800px) {
top: 70px;
left: 50px;
}
}
`;
I made a helpful interactive demonstration of what I am trying to achieve (problem code is at line 24):
https://codesandbox.io/embed/fragrant-star-m71ct
Feel free to change the breakpoint variable from 800 if you need to.
Any help is appreciated!

You could take a different approach, where your keyframes animation is instead defined as a function like this:
const slideAnim = (top, left) => keyframes`
100% {
top: ${ top };
left: ${ left };
}
`;
The function accepts input parameters that dictate the destination coordinates of top and left for that animation's final keyframe.
In your stlyed component (ie Box1), you'd then invoke slideAnim() with specific coordinates to each breakpoint, to achieve different animation behavior per breakpoint:
/*
Declare responsive styling in the Box1 component, where destination
coordinates are specified for the animate on a per-breakpoint-basis
*/
const Box1 = styled.div`
width: 20px;
height: 20px;
background-color: blue;
position: absolute;
left: -100px;
top: 80px;
&.slide {
animation: ${slideAnim('20px', '30px')} 0.4s ease-in-out forwards;
#media (max-width: ${breakpoint}px) {
animation: ${slideAnim('70px', '50px')} 0.4s ease-in-out forwards;
}
}
`;
In summary, the idea is to shift the responsive styling into your styled component (ie Box1), while defining a reusable function that contains the common/shared keyframes for each responsive breakpoint.
Here's a working example - hope that helps!

A more developed approach to use media queries with styled-components
firstly we define the screen size for each device:
const size = {
mobile: "320px",
tablet: "768px",
laptop: "1024px",
desktop: "2560px",
}
then we use css imported from styled-components to address media queries in relevant constants for each device
export const mobile = (inner) => css`
#media (max-width: ${size.mobile}) {
${inner};
}
`;
export const tablet= (inner) => css`
#media (max-width: ${size.tablet}) {
${inner};
}
`;
export const desktop= (inner) => css`
#media (max-width: ${size.desktop}) {
${inner};
}
`;
export const laptop= (inner) => css`
#media (max-width: ${size.laptop}) {
${inner};
}
`;
now lets say you are creating a styled div and you want to use media queries it. and based on the accepted answer to use keyframes
const slideAnim = (top, left) => keyframes`
100% {
top: ${ top };
left: ${ left };
}
`;
const StyledDiv= styled.div`
/* ...css */
animation: ${slideAnim('20px', '30px')} 0.4s ease-in-out forwards;
${mobile(css`
width:300px;
animation: ${slideAnim('10px', '20px')} 0.1s ease-in-out forwards;
/* ...css */
`)};
${tablet(css`
width:500px;
/* ...css */
`)};
/*... and so on */
`;

Use keyframes outside of all blocks. eg:
import { IconButton} from "#material-ui/core";
const CloseButton = styled(IconButton)`
padding: 3px !important;
outline: none;
#media only screen and (min-width: 728px) {
padding: 4px !important;
margin:0
}
`;

Related

By using styled components, how can I add some opacity to a background color that comes from an external prop?

This is my code, below it you will find the question
import { printIntrospectionSchema } from 'graphql';
import React, { useContext, useState } from 'react';
import styled from 'styled-components';
import { Context } from '../../../Context/Context';
// DATA
function DurationIntervalSelection() {
const tabsData = [
{
text: 'Duration',
},
{
text: 'Interval',
},
];
// STATE
const [selectedIndex, setselectedIndex] = useState(0);
const { selected } = useContext(Context);
console.log(selected.color, 'selected color');
// FUNCTION
const handleIndexSelection = (index) => {
setselectedIndex(index);
};
return (
<Container>
<TabsContainer backgroundColor={selected.backgroundColor}>
<Indicator
backgroundColor={selected.color}
style={{
left: `${selectedIndex * 50}%`,
}}
/>
{tabsData.map((tab, index) => {
return (
<Tab
style={{
color: selectedIndex === index ? 'white' : 'black',
}}
onClick={() => handleIndexSelection(index)}
key={tab.text}
>
<p style={{ zIndex: 100 }}>{tab.text}</p>
</Tab>
);
})}
</TabsContainer>
</Container>
);
}
const Container = styled.div`
margin-top: 10px;
border: 1px solid #f2f2f7;
border-radius: 10px;
box-sizing: border-box;
padding: 10px 0;
`;
const Indicator = styled.div`
width: 50%;
position: absolute;
/* z-index: -1; */
height: 100%;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
background-color: ${(props) => props.backgroundColor};
`;
const TabsContainer = styled.div`
display: flex;
width: 100%;
justify-content: space-between;
position: relative;
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2);
border-radius: 20px;
overflow: hidden;
background-color: ${(props) => props.backgroundColor};
`;
const Tab = styled.div`
display: flex;
width: 50%;
justify-content: center;
cursor: pointer;
align-items: center;
padding: 10px;
`;
export default DurationIntervalSelection;
As you can see from the above code i pass backgroundColor as a prop that takes some color from a React state.
<TabsContainer backgroundColor={selected.backgroundColor}>
And in the styled component I do:
background-color: ${(props) => props.backgroundColor};
But my question is, since i need to follow a design made by a colleague, how do i add opacity to the above background color without affecting the opacity of the whole div: TabsContainer. I mean, if i add on the TabsContainer -> opacity: 0.3 - for instance the whole TabsContainer div will be affected by the opacity, including the Tab div, while what I want is just to change opacity on the color itself so i do not affect any child. I hope it makes sense. Is there a way?
Okay if your hex value comes from your backend, you can either create your own javascript function that converts hex to rgba or you can use a library like polished. They have a function named rgba that converts a hex string and adds an alpha value to it
import { rgba } from 'polished'
const div = styled.div`
background: ${(props) => rgba(props.backgroundColor, 0.3)};
`
Or you can apply it directly to the prop
<TabsContainer backgroundColor={rgba(selected.backgroundColor, 0.3)}>
Example CodeSandbox.
Since the data from the backend where right but I could not find a solution I have realised that I could have added to
<TabsContainer backgroundColor="rgba(255,255,255, 0.5)">
A background color on the white notes with opacity so to emphasise the color behind it in a lighter way

How to Desktop View, Tablet View ,Mobile view change inside a browser ReactJS

I am new to Reactjs . How to add 3 views inside a browser,by automatically change view.
Reference Page Link
You can wrap all your content with a div and control the width of that div.
// App.js
import { useState } from 'react';
import './App.css';
function App() {
const [state, setState] = useState('desktop');
return (
<div className="App">
<button onClick={() => setState('desktop')}>Desktop</button>
<button onClick={() => setState('tablet')}>Tablet</button>
<button onClick={() => setState('phone')}>Phone</button>
<div className={`container ${state}`}>Your content here</div>
</div>
);
}
export default App;
/*App.css*/
.App {
height: 100%;
background-image: -webkit-linear-gradient(0deg, #766dff 0%, #88f3ff 100%);
}
/* General styling */
button {
padding: 0.5rem;
margin: 1rem;
border-radius: 10px;
cursor: pointer;
border: none;
}
.container {
/* General styling */
background-color: whitesmoke;
padding: 1rem;
border-radius: 5px;
margin-top: 1rem;
transition: all 1s;
/* Center the container */
margin-left: auto;
margin-right: auto;
}
.desktop {
width: 90%;
}
.tablet {
width: 768px;
}
.phone {
width: 480px;
}
/* index.css */
/* To make full width and height */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body,
#root {
height: 100%;
}
The above answer is correct. You can simply give a width to the main container to 100% and place your other container with other elements inside of it, also specify width using #mediaqueries(" https://www.w3schools.com/css/css_rwd_mediaqueries.asp "). This way, you can control the elements inside the main container. I can say for sure that you need to try it by yourself and see how everything works, use devtools(there is toogle device toolbar).

How to Create Dynamic Media Queries with Styled-Components?

I am trying to use the following approach below to not cause memory overload:
${MediaXS} {
font-size: 0.77rem;
}
Instead of using this approach below, which causes memory problems because it is an array:
$ {MediaXS`
color: red
`}
But I don't know the correct way to export to use with the first example, what should I do?
MY CODE:
import { css } from "styled-components";
export const MediaXS = css`
#media only screen and (min-width: 320px) {
}
`;
export const MediaSM = css`
#media only screen and (min-width: 576px) {
}
`;
export const MediaMD = css`
#media only screen and (min-width: 768px) {
}
`;
export const MediaLG = css`
#media only screen and (min-width: 992px) {
}
`;
export const MediaXL = css`
#media only screen and (min-width: 1200px) {
}
`;
I believe you can do something like this:
// configure screen width thresholds
const ScreenSizes = {
DESKTOP: 992,
TABLET: 768,
PHONE: 576
}
const sizes = {
desktop: ScreenSizes.DESKTOP,
tablet: ScreenSizes.TABLET,
phone: ScreenSizes.PHONE
}
// iterate through sizes and create a media template
const media =
Object
.keys(sizes)
.reduce((acc, label) => {
acc[label] = (...args) => css`
#media (max-width: ${sizes[label] / 16}em) {
${css(...args)}
}
`
return acc
}, {});
// use media methods with styled-component instead of raw #media queries
const StyledContent = styled.div`
width: 100%
${media.desktop`
padding: 10px;
`}
${media.tablet`
padding: 15px;
max-width: 700px;
`}
${media.phone`
padding: 20px;
max-width: 900px;
`}
`;
export class Content extends React.Component {
render() {
return (
<StyledContent>
{ this.children }
</StyledContent>
);
}
}
Credits to Ross Bulat, taken from this article:
https://medium.com/#rossbulat/creating-themes-in-react-with-styled-components-6fce744b4e54
I'm using dinamics media with styled-componets of this form:
const tabletStartWidth = 600;
const desktopStartWidth = 768;
const extraLargeStartWidth = 1300;
const responsiveDesign = {
tablet: `#media (min-width: ${tabletStartWidth}px) and (max-width: ${desktopStartWidth - 1}px)`,
desktop: `#media (min-width: ${desktopStartWidth}px) and (max-width: ${extraLargeStartWidth - 1}px)`,
extraLarge: `#media (min-width: ${extraLargeStartWidth}px)`,
};
and in the component e.g:
${responsiveDesign.desktop} {
padding: 5px;
justify-content: "center";
text-align: center;
}
${responsiveDesign.extraLarge} {
padding: 10px;
}
And it works for me.

How to create a "connect the dot" style map in react with canvas?

I need to create a "connect the dot" style react component which displays a "map" of dots connected by a "path" advancing through different stages, according to some data defined in a JSON. My problem is not with the programming logic, but how should I draw it, either with html canvas,css,svg or a combination of both:
I already have a component ready with the background, but I don't know how to draw the dots:
Some code:
class MapCanvas extends Component {
render() {
return (
<div className={classNames("exams-map-canvas", this.props.stage)}>
</div>
);
}
}
.exams-map-canvas {
position: relative;
top: 20%;
height: 100%;
margin: 0 auto;
background-size: cover;
border-radius: 10px;
#media (min-width: 700px) {
& {
top: 15%;
height: 550px;
}
}
// Large screen
#media (min-width: 1025px) and (max-width: 1280px) {
& {
height: 550px;
}
}
#media (min-width: 1400px) {
& {
height: 650px;
}
}
#media (min-width: 1700px) {
& {
height: 850px;
}
}
How can I add dots connected by lines to this?
I would use SVG to draw on top of the image. It maps very well with JSX and its quite powerful.
Here is my take on how you could build your components:
https://codesandbox.io/embed/connect-the-dot-style-map-5d7nm
On the example, I took the liberty of modifying the style of the nodes and the links if they were "done".
One thing, whatever you choose to draw on the page, look at the d3 library. It has a ton of helper functions to create designs online. On my example, I am using the d3-scale module to create a scale to map from an arbitrary 0-100 domain to the width and height of the image. Now you can store the relative location of the dots, and adapt them to any map's width and height. You can also play with the SVG viewPort to handle how much to show.
I hope this helps.
class MapCanvas extends Component {
render() {
return (
<div className={classNames("exams-map-canvas", this.props.stage)}>
</div>
);
}
}
.exams-map-canvas {
position: relative;
top: 20%;
height: 100%;
margin: 0 auto;
background-size: cover;
border-radius: 10px;
#media (min-width: 700px) {
& {
top: 15%;
height: 550px;
}
}
// Large screen
#media (min-width: 1025px) and (max-width: 1280px) {
& {
height: 550px;
}
}
#media (min-width: 1400px) {
& {
height: 650px;
}
}
#media (min-width: 1700px) {
& {
height: 850px;
}
}

Why is my media tag not working with my styled ocmponent?

I am trying to add a #media to a styled-component. Here is my Component:
const Image = styled.img`
display: none;
#media(width >= 600px) {
display: block;
};
`;
The #media is never triggered. What am I doing wrong? How do we get #media tags to work with styled-components?
Your media query is wrong, as you would do for standard CSS you need to specify the min-width like:
#media (min-width: 600px) {
display: block;
}

Resources