Chakra-ui heading size by heading level in theme - reactjs

I'm trying to define the font size for h elements in a Chakra-UI theme, and for some reason, no matter what I do it's not overriding what I'm assuming is the base theme responsive definitions.
This is in NextJS, pulling content from DatoCMS headless CMS, so the actual instantiation of the h elements looks like this:
...
renderRule(isHeading, ({ node, children, key }) => {
return (
<Heading key={key} as={`h${node.level}`} pt={6} pb={4}>
{children}
</Heading>
);
}),
...
If I change it there to, say, <Heading key={key} as={`h${node.level}\`} pt={6} pb={4} size=('5xl')>, it works fine. My theme also works fine in general (e.g., naming brand color variables, setting the font weight on Heading elements), but for some reason, I can't get it to set the size of the headings.
The closest I've gotten is setting the size in the global object:
const styles = {
global: (props) => ({
h1: { fontSize: [null, null, '5xl'] }
})
}
Doing the above at least makes it to the browser, but it gets overridden. In dev tools, the computed css shows
#media screen and (min-width: 48em)
.css-fcs9e9 {
font-size: var(--chakra-fontSizes-4xl);
line-height: 1.2;
}
.css-fcs9e9 {
font-family: var(--chakra-fonts-heading);
font-weight: var(--chakra-fontWeights-light);
// font-size: var(--chakra-fontSizes-3xl); shown as overridden
line-height: 1.33;
padding-top: var(--chakra-space-6);
padding-bottom: var(--chakra-space-4);
}
#media screen and (min-width: 48em)
h1 {
// font-size: var(--chakra-fontSizes-5xl); shown as overridden
}
I've also tried
setting a single value in textStyles:
const textStyles = {
h1: {
fontSize: '5xl'
}
}
setting an array in textStyles:
const textStyles = {
h1: {
fontSize: [ null, null, '5xl' ]
}
}
setting the baseStyle at the component level:
const Heading = defineStyleConfig({
baseStyle: {
fontWeight: 'light', // this works
textStyles: { h1: [ null, null, '5xl' ] } // this doesn't work (also for singular `textStyle`
}
})
but none of those three even make it to the browser.
What's the right way to set the text size at the theme level?

Related

How to listen to width of page - React

I have 2 headers. I want to render the first one when width > 768px, and second one when < 768.
I tried this :
const [isMobileResponsive, setIsMobileResponsive] = useState(false);
useEffect(() => {
setIsMobileResponsive(window.innerWidth < 768);
} , [window.innerWidth]);
return (
!isMobileResponsive ?
<p>header1</p>
:
<p>header2</p>
)
The problem with this : when I go to chrome responsive feature, and resize the size of the screen, React does not re-render and don't change the header.
I have to reload the page to get the new Header.
How can I do this ?
Thank you very much.
To track the window's width in react you can make a custom hook to handle window resizing such as it is done here.
But in this concrete case I think you would be better off using CSS media queries with a mobile first approach.
.navbar-desktop {
display: none;
}
#media only screen and (min-width: 768px) {
/* For desktop: */
.navbar-desktop {
display: block;
}
.navbar-mobile {
display: none;
}
}
The code above hides the .navbar-desktop by default only showing it if the width of the viewport is greater than 768. Doing the exact opposite for the .navbar-mobile.
Simply add resize Event Listener
const App = () => {
useEffect(() => {
window.addEventListener('resize', handleResize);
}, []);
const handleResize = (e) => {}
return <div>Test</div>;
};
You can do it by creating a class somewhere and writing display: none; inside of it. After doing so you can get the screen width with screen.width.
const viewerFunc = (yourContainerId) => {
const overview = document.getElementById(yourContainerId);
overview.classList.toggle('d-none');
}
document.getElementById("demo").innerHTML =
"Screen width is " + screen.width;
or you can do it as #David Machado suggested with CSS.
display-desktop{
// your codes
display: none;
}
display-mobile{
// your codes
display: flex;
}
// Mobile version of your app
#media only screen and (min-width: 768px) {
display-desktop{
display: flex;
}
display-mobile{
display: none;
}
}

React - Listening for div width change

I am trying to set a listener to determine the width of a particular div within my application. If the div is less than 800px wide, I'd like to set the style to display: none. I'm able to do it as is, but it doesn't listen to it and only removes the div if I refresh the page.
const [style, setStyle] = useState({});
const documentRef = useRef(null);
useEffect(() => {
if (documentRef.current.offsetWidth < 800) {
const updatedStyle = {
display: 'none',
}
setStyle(updatedStyle);
}
}, [documentRef.current]);
You can use CSS media query to hide something when below 800 px.
#media only screen and (max-width: 800px) {
show-lg-only {
display: none;
}
}

How to set breakpoints when overriding theme variables in createMuiTheme

I'm trying to set the default theme typography font sizes to change based on breakpoints. For example, when the breakpoint is at the {xs} value, change theme.typography.h4.fontSize to '1.5rem'. At all other breakpoints, leave it at the default value ('2.125rem').
I can do this easily in components using the following code example:
const useStyles = makeStyles(theme => ({
title: {
[theme.breakpoints.down('xs')]: {
fontSize: '1.5rem',
}
},
}))
However, this means having to add the same code to every single component that has <Typography variant='h4'> in it. If I decide to change the value from '1.5rem' to '1.25rem', I would need to locate every single instance of <Typography variant='h4'> and change it manually.
Is there any way to add breakpoints to createMuiTheme so it applies to all instances?
I have attempted the following, which does not work:
const defaultTheme = createMuiTheme()
const theme = createMuiTheme({
typography: {
h4: {
[defaultTheme.breakpoints.down('xs')]: {
fontSize: '1.5rem'
}
}
}
})
You can use custom breakpoints with createBreakpoints.
Here is an example changing the MuiButton
import createBreakpoints from '#material-ui/core/styles/createBreakpoints'
const customBreakpointValues = {
values: {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
},
}
const breakpoints = createBreakpoints({ ...customBreakpointValues })
const theme = createMuiTheme({
breakpoints: {
...customBreakpointValues,
},
components: {
MuiButton: {
styleOverrides: {
root: {
padding: '10px',
[breakpoints.up('lg')]: {
padding: '20px',
},
},
},
},
},
})
CORRECTION: Initially I indicated that your solution didn't work because you were using an impossible condition, but I was incorrect. "down" from xs is inclusive of xs, so it does what you would want (matches 0px to 600px).
I'm not sure what I did in my initial testing that led me astray, but one thing that can cause confusion is if you have multiple down thresholds. In my own testing, I had an easier time avoiding shooting myself in the foot (e.g. by having a breakpoints.down("xs") followed by a later breakpoints.down("sm") that trumped the "xs" settings) by using breakpoints.only.
From https://material-ui.com/layout/breakpoints/#breakpoints
xs, extra-small: 0px or larger
sm, small: 600px or larger
Here is an updated sandbox that shows a little more clearly what is happening:

Rotate arrow indicator in React-select v2

I'm using React Select v2 in my project with Styled Components and I need to be able to turn the arrow indicator upside down when the menu is open, which was supported in v1.
I kinda managed to do it by doing this:
css`
&.react-select__control--is-focused {
& .react-select__indicators {
& .react-select__dropdown-indicator {
transform: rotate(180deg);
}
}
}
`;
Problem is that, if I press the arrow to open the menu and click on it again to close it, the arrow stays upside down because the select is still focused, which feels a bit weird in terms of UIX.
Is there a proper way to rotate it based on the state of the menu? I looked for something in the documentation but I couldn't find it.
Maybe I missed it, if someone could point me in the right direction, that'd be awesome!
Thanks!
Technically you can use the style-in-JS props of the v2. Like the following example:
dropdownIndicator: (base, state) => ({
...base,
transition: 'all .2s ease',
transform: state.isFocused ? 'rotate(180deg)' : null
})
It seems that the isFocused state isn't bind with the isMenuOpen state but with the real focus state of the container.
A solution is to set closeMenuOnSelect={false} so the user would have to click outside the select and your arrow will flip back.
Or you could change the className props using onMenuOpen and onMenuClose by adding a specific suffix to target your animation.
UPDATE
You can directly access the menuOpen props via the state so no need to manually add class like the following:
dropdownIndicator: (base, state) => ({
...base,
transition: 'all .2s ease',
transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : null
})
PLEASE NOTE THAT
In react-select v2.3 a control--menu-is-open has been added directly in the code.
This worked for me
<select styles={{
dropdownIndicator: (provided, state) => ({
...provided,
transform: state.selectProps.menuIsOpen && "rotate(180deg)"
})
}}
/>
So, based on Laura's response, my solution was to use the onMenuClose and onMenuOpen to set the state of a property in my styled component.
const indicatorStyle = (props: StyledSelectProps & DropdownProps<{}>) => css`
& .react-select__indicators {
& .react-select__dropdown-indicator {
transition: all .2s ease;
transform: ${props.isOpen && "rotate(180deg)"};
}
}
`;
This function is called inside of my styled component's css.
And then in the component I call my styled component, I control the state:
export class Dropdown<TValue> extends React.Component<DropdownProps<TValue>> {
public state = { isOpen: false };
private onMenuOpen = () => this.setState({ isOpen: true });
private onMenuClose = () => this.setState({ isOpen: false });
public render() {
const { ...props } = this.props;
const { isOpen } = this.state;
return (
<StyledSelect {...props} isOpen={isOpen} onMenuOpen={this.onMenuOpen} onMenuClose={this.onMenuClose} />
);
}
}
A bit convoluted but it works for now.
This worked for me.
dropdownIndicator: (base, state) => ({
...base,
transform: state.selectProps.menuIsOpen ? 'rotate(-90deg)' : 'rotate(0)',
transition: '250ms',
}),
i solved this issue like this.
mostly you have to play arround with these in your css to do stuff on certain conditions
--is-focused
--menu-is-open
--is-disabled
--is-selected
styled component css
.paginatorPageSizeCustomSelectPreffix__indicator {
svg {
color: var(--black);
height: 18px;
width: 18px;
transform: rotate(180deg);
transition: 0.2s;
}
}
.paginatorPageSizeCustomSelectPreffix__control--menu-is-open {
.paginatorPageSizeCustomSelectPreffix__indicator {
svg {
color: var(--black);
height: 18px;
width: 18px;
transform: rotate(360deg);
transition: 0.2s;
}
}
}
}

material-ui: AppBar: strategy for restricting an image height to AppBar height?

can anyone provide guidance around an idiomatic way to place an image in an AppBar and have it be restricted to the standard material height (e.g. 64px for a desktop)?
i'm currently using material-ui#next (1.0.0-beta.2 currently).
i have found that something like:
<AppBar>
<Toolbar>
<IconButton color="contrast" aria-label="Menu">
<MenuIcon />
</IconButton>
<img src={logo} style={{height: 64}}/>
<Typography type="title" color="inherit">
React Scratch
</Typography>
</Toolbar>
</AppBar>
works well.
the actual logo is a png file with a height greater than 64, so if i don't ratchet it down, it expands the height of the AppBar out of Material spec.
in the current master branch version of src/styles there is a getMuiTheme.js which seems to deliver this height readily, but in the #next version i am looking at, that file doesn't even exist and tbh, i can't easily determine how that height is being set anymore.
i found that the AppBar is currently being renovated for composability, so that churn might make it challenging to answer this question, but just in case anyone has a good handle on this, i figured i would toss the question out there.
thanks!
In all cases I've seen, an AppBar is implemented with a Toolbar as it's first child. The Toolbar's stylesheet dictates it's height based on the breakpoints defined in the theme.
Take a look here: https://github.com/callemall/material-ui/blob/v1-beta/src/Toolbar/Toolbar.js
You can use a similar approach to define a stylesheet with a class for your AppBar images that varies the height for the applicable breakpoints. Then when rendering the component, apply the class to your image.
Note: if you use the withStyles HOC, as is done in the Toolbar, AppBar etc, the classes defined in that stylesheet will be available through a prop named classes.
You are right about the AppBar's need for composability, but that issue has not been solved yet, and this is the beta branch anyway. When it is solved, there should be a better solution that would be worth migrating towards.
I hope this answer helps. I would have added code samples but I am answering from my phone while waiting in a grocery store parking lot. If I get a chance I will update this answer.
Here's one approach, duplicating the styles in a new reusable component:
import createStyleSheet from 'material-ui/styles/createStyleSheet';
import withStyles from 'material-ui/styles/withStyles';
// define these styles once, if changes are needed because of a change
// to the material-ui beta branch, the impact is minimal
const styleSheet = createStyleSheet('ToolbarImage', theme => ({
root: {
height: 56,
[`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
height: 48,
},
[theme.breakpoints.up('sm')]: {
height: 64,
},
},
}));
// a reusable component for any image you'd need in a toolbar/appbar
const ToolbarImage = (props) => {
const { src, classes } = this.props;
return (
<img src={src} className={classes.root} />
);
};
// this higher order component uses styleSheet to add
// a classes prop that contains the name of the classes
export default withStyles(styleSheet)(ToolbarImage);
Another approach is to add the standard toolbar heights to the theme as business variables, override the root class for all Toolbars so that it makes use of them, and use the theme whenever you need to reference them again:
// define the standard heights in one place
const toolbarHeights = {
mobilePortrait: 56,
mobileLandscape: 48,
tabletDesktop: 64,
};
// create the theme as you normally would, but add the heights
let theme = createMuiTheme({
palette: createPalette({
primary: blue,
accent: pink,
}),
standards: {
toolbar: {
heights: toolbarHeights,
},
},
});
// recreate the theme, overriding the toolbar's root class
theme = createMuiTheme({
...theme,
overrides: {
MuiToolbar: {
// Name of the styleSheet
root: {
position: 'relative',
display: 'flex',
alignItems: 'center',
minHeight: theme.standards.toolbar.heights.mobilePortrait,
[`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
minHeight: theme.standards.toolbar.heights.mobileLandscape,
},
[theme.breakpoints.up('sm')]: {
minHeight: theme.standards.toolbar.heights.tabletDesktop,
},
},
},
},
});
Then you can reference these heights in any stylesheet you create because they're part of the theme.
UPDATED FOLLOWING THE RELEASE OF 1.0.0-beta.11:
There is now a toolbar mixin available on the theme that provides the toolbar minHeight for each breakpoint. If you need to style an element relative to the standard height of the AppBar component, you can use this object to build your own styles:
const toolbarRelativeProperties = (property, modifier = value => value) => theme =>
Object.keys(theme.mixins.toolbar).reduce((style, key) => {
const value = theme.mixins.toolbar[key];
if (key === 'minHeight') {
return { ...style, [property]: modifier(value) };
}
if (value.minHeight !== undefined) {
return { ...style, [key]: { [property]: modifier(value.minHeight) } };
}
return style;
}, {});
In this example, toolbarRelativeProperties returns a function that will return an object that can be spread into your style object. It addresses the simple case of setting a specified property to a value that is based on the AppBar height.
A simple usage example would be the generation of a dynamic CSS expression for height calculation, which is depending on the standard height of the AppBar:
const componentStyle = theme => ({
root: {
height: toolbarRelativeProperties('height', value => `calc(100% - ${value}px)`)(theme)
}
});
The generated style definition might look like this:
{
height: 'calc(100% - 56px)',
'#media (min-width:0px) and (orientation: landscape)': {
height: 'calc(100% - 48px)'
},
'#media (min-width:600px)': {
height: 'calc(100% - 64px)'
}
}

Resources