How can I use media query with emotion/styled/macro? - reactjs

I cannot find any code that using styled macro with media query and don't understand why It's so rare to find code using emotion/styled/macro.
I know it allows css style in object literal
const StyledUl = styled("ul")(
{
backgroundColor: "#fff",
height: "200px",
width: "100%",
overflowY: "scroll",
position: "absolute",
margin: 0,
padding: "5px",
boxShadow: "-1px 15px 34px -21px rgba(0,32,86,0.21)",
boxSizing: "border-box",
borderRadius: "8px",
zIndex: 9999,
}
)
But how can I use media query?
And where can I find documentation about emotion/styled/macro?

Nothing special is required. Just add a #media ... query as a property:
import styled from "#emotion/styled/macro";
const StyledUl = styled("ul")({
"#media (max-width: 600px)": {
backgroundColor: "#000",
color: "#fff"
},
backgroundColor: "#fff",
height: "200px",
width: "calc(100% - 40px)",
overflowY: "scroll",
position: "absolute",
margin: 0,
padding: "5px",
boxShadow: "-1px 15px 34px -21px rgba(0,32,86,0.21)",
boxSizing: "border-box",
borderRadius: "8px",
zIndex: 9999
});
export default StyledUl;
Optionally, you can use a template literal to style a styled.HTMLElement:
import styled from "#emotion/styled/macro";
const StyledUlTemplate = styled.ul`
#media (max-width: 600px) {
background-color: #000;
color: #fff;
}
background-color: #fff;
height: 200px;
width: calc(100% - 40px);
overflow-y: scroll;
position: absolute;
top: 60%;
margin: 0;
padding: 5px;
box-shadow: -1px 15px 34px -21px rgba(0, 32, 86, 0.21);
box-sizing: border-box;
border-radius: 8px;
z-index: 9999;
`;
export default StyledUlTemplate;
Demo (drag the middle bar left/right to resize the Browser tab to trigger the style changes):

Related

How do I stop my image from stretching on larger screen sizes?

I have a hero section that displays an image loaded from an API. It works fine on smaller screen sizes but stretches on bigger screens. How do I prevent this from happening and make sure that the image doesn't look stretched?
Apologies in advance if if my code is confusing, this was my first React project.
Thanks!
<div className="hero-section">
<Slideshow movies={movies}/>
</div>
.hero-section {
width: 100vw;
height: 100vh;
}
<div className="slideshow">
<div className="slideshowSlider" style={{ transform: `translate3d(${-index * 100}%, 0, 0)` }}>
{posters.map((poster, index) => (
<img src={'https://image.tmdb.org/t/p/w500' + poster} className="slide" key={index} style={{ poster }}/>
))}
</div>
.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 100vw;
background-color: #222222;
}
.slideshowSlider {
white-space: nowrap;
transition: ease 1000ms;
height: 50vh;
width: 100vw;
object-fit: cover;
}
.slide {
display: inline-block;
height: 50vh;
width: 100%;
}
You should change height: 50vh; to height: auto; in .slide styles.
.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 100vw;
background-color: #222222;
}
.slideshowSlider {
white-space: nowrap;
transition: ease 1000ms;
height: 50vh;
width: 100vw;
object-fit: cover;
}
.slide {
display: inline-block;
height: auto; // fixed!
width: 100%;
}

How to add a label to a border in mui?

I would like to have a list wrapped in a border which looks and behaves the same as a textfield border:
Example textfield and list which should have both same border.
In the image, the border around the list looks similar than the one around the textfield but most notably, the label is missing. How can I add the label and how would I set up the focus listeners to get the same hover and selection behaviour?
The typescript code for the list:
<List dense sx={{ borderRadius: 1, border: 1, borderColor: 'grey.600'}}>
<ListItem secondaryAction={<IconButton edge="end" aria-label="delete"><DeleteIcon /></IconButton>}>
<ListItemText primary="primary" secondary="group id"/>
</ListItem>
</List>
I am also open for alternative approaches. Thanks for the help.
Here is my answer using React and Mui (only for icon).
It relies on flex.
We have a main container that only draws its left, bottom and right borders.
Then we have a header container in charge of drawing the top border in two parts (before and after) and a section with an icon and title.
You can either pass an icon and a title, just a title, just an icon, or nothing at all.
borderedSection.js:
import React from "react";
import SvgIcon from "#mui/material/SvgIcon";
import styles from "./borderedSection.module.scss";
function BorderedSection({ icon, title, children }) {
return (
<div className={styles.mainContainer}>
<div className={styles.header}>
<div className={styles.headerBorderBefore}></div>
{(icon || title) && (
<div className={styles.headerTitle}>
{icon && <SvgIcon component={icon} />}
{title && <span className={styles.title}>{title}</span>}
</div>
)}
<div className={styles.headerBorderAfter}></div>
</div>
<div className={styles.childrenContainer}>{children}</div>
</div>
);
}
export default BorderedSection;
borderedSection.module.scss:
$border-color: #b2b2b2;
.mainContainer {
display: flex;
flex-direction: column;
max-width: 100%;
border-left: 1px solid $border-color;
border-bottom: 1px solid $border-color;
border-right: 1px solid $border-color;
border-radius: 5px;
margin: 1em;
.childrenContainer {
padding: 1em;
}
.header {
display: flex;
flex-direction: row;
width: 100% !important;
.headerBorderBefore {
border-top: 1px solid $border-color;
width: 1em;
border-top-left-radius: 5px;
}
.headerTitle {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
gap: 0.25em;
width: fit-content;
height: 2em;
margin: -1em 0.5em 0em 0.5em;
overflow: hidden;
text-overflow: ellipsis;
font-size: 1em;
font-weight: 600;
}
.headerBorderAfter {
border-top: 1px solid $border-color;
width: 1em;
flex-grow: 2;
border-top-right-radius: 5px;
}
}
}
usage:
import React from "react";
import BorderedSection from "./borderedSection";
import InfoIcon from "#mui/icons-material/Info";
function Example() {
return (
<div style={{ padding: "2em" }}>
<BorderedSection icon={InfoIcon} title="Icon and title">
<div>a first child with quite a long text</div>
<div>a second child</div>
</BorderedSection>
<BorderedSection title="Title only">
<div>a first child with quite a long text</div>
<div>a second child</div>
</BorderedSection>
<BorderedSection icon={InfoIcon} >
<div>Icon only</div>
<div>a second child with quite a long text</div>
</BorderedSection>
<BorderedSection >
<div>No icon and no title</div>
<div>a second child with quite a long text</div>
</BorderedSection>
</div>
);
}
Here is how it looks:
I hope it helps
I now managed to hack a solution which looks the same. I do still hope though that there is a clean way to do this: result.
<FormLabel style={{marginLeft: "0.71em", marginTop: "-0.71em", paddingLeft: "0.44em", zIndex: 2, width: "4.2em", backgroundColor: "#383838", position: "absolute", fontSize: "0.75em"}}>Damage</FormLabel>
<List dense sx={{ borderRadius: 1, border: 1, borderColor: 'grey.600', "&:hover": { borderColor: 'grey.200' }}}>
<ListItem secondaryAction={<IconButton edge="end" aria-label="delete"><DeleteIcon /></IconButton>}>
<ListItemText primary="primary" secondary="group id"/>
</ListItem>
</List>
I needed the same thing. As I was poking around I noticed that MUI accomplished this by using the fieldset tag. I created a quick and dirty component (OutlinedBox) to get this effect:
import React from "react";
import {Box, FormLabel} from "#mui/material";
const OutlinedBox = (props) => {
const {
label,
children
} = props;
return (
<Box>
<FormLabel
sx={{
marginLeft: "0.71em",
marginTop: "-0.71em",
paddingLeft: "0.44em",
paddingRight: '0.44em',
zIndex: 2,
backgroundColor: (theme) => theme.palette.background.default,
position: "absolute",
fontSize: "0.75em",
width: 'auto',
}}>{label}</FormLabel>
<Box
sx={{
position: 'relative',
borderRadius: theme => theme.shape.borderRadius + 'px',
fontSize: '0.875rem',
}}
>
<Box
sx={{
padding: (theme) => theme.spacing(1),
display: 'flex',
gap: (theme) => theme.spacing(1),
flexWrap: 'wrap',
overflow: 'auto'
}}
>
{children}
</Box>
<fieldset aria-hidden={"true"} style={{
textAlign: 'left',
position: 'absolute',
bottom: 0,
right: 0,
top: '-5px',
left: 0,
margin: 0,
padding: '0 8px',
pointerEvents: 'none',
borderRadius: 'inherit',
borderStyle: 'solid',
borderWidth: '1px',
overflow: 'hidden',
minWidth: '0%',
borderColor: 'rgba(255, 255, 255, 0.23)',
}}
>
<legend style={{
float: 'unset',
overflow: 'hidden',
display: 'block',
width: 'auto',
padding: 0,
height: '11px',
fontSize: '0.75em',
visibility: 'hidden',
maxWidth: '100%',
'-webkit-transition': 'max-width 100ms cubic-bezier(0.0, 0, 0.2, 1) 50ms',
transition: 'max-width 100ms cubic-bezier(0.0, 0, 0.2, 1) 50ms',
whiteSpace: 'nowrap',
}}><span>{label}</span></legend>
</fieldset>
</Box>
</Box>
);
}
export { OutlinedBox };
// Example usage: <OutlinedBox label="Test">Some content here</OutlinedBox>
I figured I'd post it here in case anyone needs the same thing and comes across this question. All the styling stuff was copied from the styles MUI was using. There may be a better way to read some of this off of the theme, so if anyone decides to use this you may want to tweak it some.

Smooth transition with React Modal

I am trying to make React modal have a smooth transition when it appears on the screen but can't seem to get the transition property to do anything. Right now, the modal pops onto the screen in a jarring manner but I'd like to get it to slowly fade in. Here is my modal with the styles:
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
transition: "opacity .5s",
width: "90%",
maxHeight: "600px",
overflow: "auto",
padding: "40px",
maxWidth: "500px",
borderRadius: "10px",
boxShadow: "0px 0px 15px 1px gray",
},
};
<Modal
ariaHideApp={false}
isOpen={modalIsOpen}
onRequestClose={() => setIsOpen(false)}
style={customStyles}
contentLabel="Example Modal"
>
Anyone have any suggestions?
I think you need to put opacity as a property itself.
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
opacity: "20%",
transition: ".5s",
width: "90%",
maxHeight: "600px",
overflow: "auto",
padding: "40px",
maxWidth: "500px",
borderRadius: "10px",
boxShadow: "0px 0px 15px 1px gray",
},
};

Transform function not working in styled components

I am trying to change the styled of "IconRadioButtonRound" component based on the "HiddenCheckBox" value.I am able to change the bakcground of "IconRadioButtonRound" with same prop i.e."checked" but not able to transform it. Please help me solve this!
Styled components:
export const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
transform: scale(8);
position: relative;
top: 40px;
left: 20px;
opacity: 0;
`;
export const IconRadioButtonRound = styled.button`
width: 63px;
height: 62px;
display: flex;
flex-direction: column;
justify-content: space-between;
border: ${props =>
props.checked ? '4px solid #daa520' : '1px solid #daa520'};
box-sizing: border-box;
border-radius: 50%;
transition: transform 500ms linear;
background: ${props => (props.checked ? 'rgba(66, 226, 248, 0.3)' : 'white')};
transform: translateY(${props => (props.checked ? '-15px' : '0')});
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
`;
Component where I am using it
<IconCheckBoxRoundButton>
<HiddenCheckbox
key={index}
name={item.itemName}
value={item.itemName}
onChange={e => handleAddOnItemChange(e)}
/>
<IconCheckBoxRound checked={item.isChecked}>
<CheckBoxIcon src={item.itemIconURL} />
</IconCheckBoxRound>
<InputLabel
for={item.itemName}
style={{ marginTop: '15px' }}
lineHeight="18px"
>
<CheckBoxIconName>
<PTSansText fontSize="18px" lineHeight="18px">
{item.itemName}
</PTSansText>
</CheckBoxIconName>
<CheckBoxIconName>
<PTSansText fontSize="16px">
<span>Rs. </span>
{item.itemPrice}
</PTSansText>
</CheckBoxIconName>
</InputLabel>
</IconCheckBoxRoundButton>

position absolute top:0, bottom: 0, right:0, left:0 and parent paddings

position absolute with all its properties in 0 makes the child element expand in the parent element, and if I put a padding in the parent element without a height the child with the position absolute cover until the of the parent padding, my theory is right or I am forgotting something here?there could be another different case?
import React from 'react';
import {View, StyleSheet} from 'react-native';
import Video from 'react-native-video';
export default function Reproductor(props) {
return (
<View style={styles.container}>
<View style={styles.videoContainer}>
<Video
source={{
uri:
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
}}
resizeMode="cover"
style={styles.video}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
height: 250,
justifyContent: 'center',
alignItems: 'center',
},
videoContainer: {
width: '90%',
paddingTop: '56.25%',
borderRadius: 20,
overflow: 'hidden',
borderWidth: 3,
backgroundColor: 'black',
},
video: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
the child (video) covers the padding
An absolutely positioned element is an element whose computed
position value is absolute or fixed. The top, right,
bottom, and left properties specify offsets from the edges of the element's containing block.
https://developer.mozilla.org/en-US/docs/Web/CSS/position
position absolute related to the edges of the parent element (= ignores parent padding/border).
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white; height: 50px;"></div>
<div style="position: absolute; background: blue; top:0; left:0; color: white;">child</div>
</div>
top: 0; right: 0; bottom: 0; left: 0; is the way to set overlay:
Example: https://www.w3schools.com/howto/howto_css_overlay.asp
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">div</div>
<div style="position: absolute; background: rgba(0, 0, 0, 0.5); top:0px; left:0px; right: 0; bottom: 0; color: white;">overlay</div>
</div>
Add space by top: 10px; for example (or em % and so on)
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">Parent</div>
<div style="position: absolute; background: blue; top:10px; left:10px; color: white;">child</div>
</div>
Or add a margin for the absolute element:
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">Parent</div>
<div style="position: absolute; background: blue; top:0px; left:0px; color: white;margin: 10px;">margin: 10px around</div>
</div>
You find more tricks/ideas but these are the basics ideas.
Thank you I understand, but you said that it cover padding and border (position absolute related to the edges of the parent element (= ignores parent padding/border).), and I try to cover the border too but it just ignore the padding not the border, what is my mistake here?
export default function MainScreen(props) {
return (
<View style={style.container}>
<View style={style.element} />
</View>
);
}
const style = StyleSheet.create({
container: {
borderWidth: 3,
padding: 40,
alignSelf: 'center',
width: 200,
height: 200,
backgroundColor: 'grey',
},
element: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
width: 100,
height: 100,
backgroundColor: 'purple',
},
});

Resources