-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typography - Feature request: font size responsiveness #11452
Comments
@PolGuixe What's preventing from doing it now? The |
@oliviertassinari you are right. I just was wondering if you think it would be useful to have it integrate it in Material-UI. What I am doing right now is: const theme = createMuiTheme({
palette,
typography,
});
const coef = 0.1;
const modifyRem = (value, coef) => {
return `${parseFloat(value) * (1 + coef)}rem`;
};
each(theme.typography, (variant, variantName) => {
if (typeof variant !== 'object') {
return variant;
}
theme.typography[variantName] = {
...variant,
fontSize: modifyRem(variant.fontSize, -coef * 5),
[theme.breakpoints.up('sm')]: {
fontSize: modifyRem(variant.fontSize, -coef * 2.5),
},
[theme.breakpoints.up('md')]: {
fontSize: modifyRem(variant.fontSize, -coef * 1),
},
[theme.breakpoints.up('lg')]: {
fontSize: modifyRem(variant.fontSize, 0),
},
[theme.breakpoints.up('xl')]: {
fontSize: modifyRem(variant.fontSize, coef),
},
};
});
export default theme; Note 1: the numbers are a bit random as I couldn't find any guidelines from Material For us it works, because having a |
@PolGuixe This sounds too opinionated. You couldn't find this logic on Bootstrap, on Ant Design, on Semantic-UI, on Blueprint, on Fabric. |
I have added the |
Ok, fair enough 😉 |
I think that we should add an example in the documentation with the best strategy. |
@oliviertassinari Cool I can do it ;) Do you still think that the approach above is the best? Does anyone have other ideas? We are using the approach above in production and it works. Every project has a slightly different configuration for their theme, font selection and look and feel. Is there a default config we would like to have? |
@PolGuixe I have found two interesting approaches using a different tradeoff:
What do you think of them? |
@oliviertassinari If we are happy to use I agree with the approach to isolate the helpers. I see the function working like:
This function will be used to implement most of the Typography responsiveness. Then the user will be always able to do manual tweaks:
|
@PolGuixe I was thinking of the following: import { fluidRange } from '@material-ui/css-helpers';
import { createMuiTheme } from '@material-ui/core';
const theme = createMuiTheme();
function remToPx(value) {
return Math.round(parseFloat(value) * 16);
}
['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'subtitle1', 'subtitle2', 'body1', 'body2', 'buttonNext', 'captionNext', 'overline'].forEach(variant => {
const style = theme.typography[variant];
const pixelFontSize = remToPx(style.fontSize);
if (pixelFontSize <= 20) {
return;
}
theme.typography[variant] = {
...style,
...fluidRange({
cssProperty: 'fontSize',
min: pixelFontSize * 0.5,
max: pixelFontSize,
lowerRange: 300,
higherRange: theme.breakpoints.values.md, // 960
}),
};
}) It focuses on providing a lower level helper, without being opinionated on how the responsive style should behave. What do you think? |
I think is a good approach and it can work. I'll have to test it to check the outputs. import { fluidRange } from '@material-ui/css-helpers';
import { createMuiTheme } from '@material-ui/core';
const theme = createMuiTheme();
function remToPx(value) {
return Math.round(parseFloat(value) * 16);
}
Object.entries(theme.typography).forEach((variantName, variant) => {
if (typeof variant !== 'object') {
return;
}
const pixelFontSize = remToPx(variant.fontSize);
if (pixelFontSize <= 20) {
return;
}
theme.typography[variantName] = {
...variant,
...fluidRange({
cssProperty: 'fontSize',
min: pixelFontSize * 0.5,
max: pixelFontSize,
lowerRange: 300,
higherRange: theme.breakpoints.values.md, // 960
}),
};
}) To encapsulate the logic as a function we can do the following: // ../css-helpers/makeTypographyResponsive.js
import {fluidRange} from './fluidRange'
function remToPx(value) {
return Math.round(parseFloat(value) * 16);
}
const makeTypographyResponsive = (theme, options = {minFontSize:20, resizeFactor: 0.5, lowerRange: 300, higherRange: 960}) => {
Object.entries(theme.typography).forEach((variantName, variant) => {
if (typeof variant !== 'object') {
return;
}
const pixelFontSize = remToPx(variant.fontSize);
if (pixelFontSize <= options.minFontSize) {
return;
}
theme.typography[variantName] = {
...variant,
...fluidRange({
cssProperty: 'fontSize',
min: pixelFontSize * options.resizeFactor,
max: pixelFontSize,
lowerRange: options.lowerRange,
higherRange: options.higherRange,
}),
};
})
}
And use it as: import { makeTypographyResponsive } from '@material-ui/css-helpers';
import { createMuiTheme } from '@material-ui/core';
const theme = makeTypographyResponsive(createMuiTheme()); Would you create this function or just put the logic as an example? |
I suggest the following solution: import createBreakpoints from "@material-ui/core/styles/createBreakpoints";
const breakpoints = createBreakpoints({});
const styles = {
"@global": {
html: {
[breakpoints.up("xs")]: {
fontSize: "8px"
},
[breakpoints.up("sm")]: {
fontSize: "12px"
},
[breakpoints.up("md")]: {
fontSize: "14px"
},
[breakpoints.up("lg")]: {
fontSize: "16px"
}
}
}
};
export default withStyles(styles)(MyApp); It's working for me. Uses withStyles in the main component and applies everywhere without any changes. Hope it helps. |
@jalcalav I believe we are looking for a solution that integrates with the |
@jalcalav I wouldn't recommend changing the |
@oliviertassinari has this helper been implemented? import { fluidRange } from '@material-ui/css-helpers'; |
@PolGuixe No, but I think that we should! I love the potential. |
fluidRange({cssProperty, min, max, range=[300, 960]}){
// calculate liner regression
const factor = (max-min)/(range[range.lenght-1]-range[0]);
const constant = min - factor*range[0];
//add media queries for each range value
range.each(value =>{
style[`@media (max-width:${value}px)`]={
[cssProperty]: `${factor*value + constant}px`
}
})
return style;
} should it work along the lines of this logic? Disclaimer: not tested, just a draft 😉 |
@PolGuixe I have made a proof of concept with a simple version (we could support different units). function fluidRange({ cssProperty, min, max, lowerRange = 400, higherRange = 960 }) {
const factor = Math.round(((max - min) / (higherRange - lowerRange)) * 10000) / 10000;
return {
[cssProperty]: `${min}px`,
[`@media (min-width:${lowerRange}px)`]: {
[cssProperty]: `calc(${min}px (100vw - ${lowerRange}px) * ${factor})`,
},
[`@media (min-width:${higherRange}px)`]: {
[cssProperty]: `${max}px`,
},
};
}
function remToPx(value) {
return Math.round(parseFloat(value) * 16);
}
function responsiveTypography(theme, { minFontSize, scale, ...other }) {
const output = theme;
output.typography = { ...theme.typography };
[
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'subtitle1',
'subtitle2',
'body1',
'body2',
'buttonNext',
'captionNext',
'overline',
].forEach(variant => {
const style = output.typography[variant];
const pixelFontSize = remToPx(style.fontSize);
if (pixelFontSize <= minFontSize) {
return;
}
output.typography[variant] = {
...style,
...fluidRange({
cssProperty: 'fontSize',
min: Math.max(minFontSize, Math.round(pixelFontSize * scale)),
max: pixelFontSize,
...other,
}),
};
});
return output;
} You would use it like this: function getTheme(uiTheme) {
- const theme = createMuiTheme({
+ let theme = createMuiTheme({
direction: uiTheme.direction,
nprogress: { color: uiTheme.paletteType === 'light' ? '#000' : '#fff' },
palette: { ...uiTheme.paletteColors, type: uiTheme.paletteType },
typography: { useNextVariants: true },
});
+ theme = responsiveTypography(theme, {
+ minFontSize: 14,
+ scale: 0.7,
+ });
+
// Expose the theme as a global variable so people can play with it.
if (process.browser) {
window.theme = theme; It's definitely something we could wrap in a |
I wanted to help but there is something I don't understand re: vertical rhythm. @oliviertassinari you quoted this nice article which makes rhythm seem easy (as does the Gutenberg package by the same author), but the approach there relies on all line heights being a multiple of (1 or 0.5)x(base line height). I looked at some rhythm preserving css generators and they all seem to do it this way (*). But material-ui gives line heights 96, 60, 49.916, 39.78, 31.9167 etc (px) and I'm not finding any pattern. Edit: unless 4px (0.25 of base line height) is the "rhythm" - and somehow ignoring the ~50px case of h3 - as per minimum requirement of Material Design (**). Presumably if fonts are made responsive this API would allow users to easily preserve vertical rhythm? Or at least the 4px MD requirement? (**) 0, 1 [presumably 'dp' which equals px on web]. |
Currently, the Typography variants are quite rigid. It will be really useful if they could become responsive e.g. different font-sizes according to the screen size.
What do you think? Would you consider a PR in that direction? 🤔
The text was updated successfully, but these errors were encountered: