Skip to content
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

Allow configuration for using px instead of rem #1232

Closed
jaschaio opened this issue Nov 21, 2019 · 41 comments
Closed

Allow configuration for using px instead of rem #1232

jaschaio opened this issue Nov 21, 2019 · 41 comments

Comments

@jaschaio
Copy link

I don't really know why the previous issues #800 and #968 got closed without a real response.

There are as much valid use cases for using rem as there are for using px.

Lets take the simple example of a website builder which has two seperate user interfaces. One that includes all the App Components designed using Tailwind, and another which includes the user generated content. If I would like to scale up or down only the user generated content area of the UI by modifying the font-size: 16px property on the body element, it only works if the rest of the user interface has a fixed sizes using px instead of rem. If everything uses rem I can't change the size of things independently of each other.

@adamwathan
Copy link
Member

You can configure Tailwind to use whatever units you want, just open up your tailwind.config.js file and and go to town.

https://tailwindcss.com/docs/theme

We can only provide one default configuration and the default configuration we've chosen to provide uses rem to size things. If you want to use pixels you can provide your own pixel-based configuration.

@stuyam
Copy link

stuyam commented Sep 8, 2020

I have an app where we are running bootstrap 3 which had the root font size set to 14px. The current rems are based off the assumption you use a 16px root font size. I know the config can be updated to change all of these values. However I love keeping the config as stock as possible so when tailwinds updates or adds other values or properties those new ones will just work. It would be great if there were a "defaultUnit" or "unit" or something setting in tailwind that defaults to "rem" but could be set to "px".

I love tailwind because it can be so light weight and can be dropped into any project, but so much of it's config assumes that project already uses a root font size of 16px. I think it could be a lot easier to add to existing projects if there was an easy switch for pixels.

For now Ill copy and past config and convert by hand ¯_(ツ)_/¯

@risingblock
Copy link

I had this problem as well, so I did the conversion for font size, spacing and line height (assuming base 16px). I'll leave it here to save somebody some time. <3 tailwind

module.exports = {
  theme: {
    fontSize: {
      xs: '12px',
      sm: '14px',
      base: '16px',
      lg: '18px',
      xl: '20px',
      '2xl': '24px',
      '3xl': '30px',
      '4xl': '36px',
      '5xl': '48px',
      '6xl': '60px',
      '7xl': '72px',
    },
    spacing: {
      px: '1px',
      0: '0',
      0.5: '2px',
      1: '4px',
      1.5: '6px',
      2: '8px',
      2.5: '10px',
      3: '12px',
      3.5: '14px',
      4: '16px',
      5: '20px',
      6: '24px',
      7: '28px',
      8: '32px',
      9: '36px',
      10: '40px',
      11: '44px',
      12: '48px',
      14: '56px',
      16: '64px',
      20: '80px',
      24: '96px',
      28: '112px',
      32: '128px',
      36: '144px',
      40: '160px',
      44: '176px',
      48: '192px',
      52: '208px',
      56: '224px',
      60: '240px',
      64: '256px',
      72: '288px',
      80: '320px',
      96: '384px',
    },
    extend: {
      lineHeight: {
        3: '12px',
        4: '16px',
        5: '20px',
        6: '24px',
        7: '28px',
        8: '32px',
        9: '36px',
        10: '40px',
      },
    },
  },
}

@jasontxf
Copy link

Thanks, @risingblock Just the thing I'm looking for :D

@nitaking
Copy link

@risingblock me too!

@joshualukecaine
Copy link

@risingblock Thanks dude!

@yellow1912
Copy link

I'm wondering if there is a way to grab the default configs, loop through it, and generate the px based config from that? It would save much time, especially when tailwind base is constantly updated.

@wenerme
Copy link

wenerme commented Jul 6, 2021

Have the same problem, when using shadow dom, the rem is controlled by outside html, the sdk use tailwindcss, so can not use rem, unless the consumer use 16px based rem too.

@wenerme
Copy link

wenerme commented Jul 6, 2021

@yellow1912 the default is here https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js#L7

@igdev116
Copy link

@risingblock Thanks your sharing! I have a problem when I use "rem" of Tailwind. For example, "w-8" have to be "32px" equal "2rem" of Tailwind but when I go to my Chrome browser, it missing "2px" or "4px" and only have "30px", I have not configured anything yet 😥

@zbecknell
Copy link

FYI: if anyone wants to use the default config to convert to pixels, this VS Code plugin allows quick conversion: https://marketplace.visualstudio.com/items?itemName=sainoba.px-to-rem

You can select all and convert at once. In my case I decided to go to the sections I needed and convert one-by-one to then copy over to my config file.

This would be such a nice option to have baked in to know you don't have to manually specify these values (in case future properties are added). Wonder if a plugin can change such things or if that's outside the reach of plugins...

@joshua-scott
Copy link

Here's an update to @risingblock's config for Tailwind v3. It converts everything in the default config that uses rem to px (assuming 16px base). I used the px-to-rem extension suggested by @zbecknell's to do it quickly.

theme: {
  /**
   * Default tailwind values converted from rem to px:
   * */
  borderRadius: {
    none: "0px",
    sm: "2px",
    DEFAULT: "4px",
    md: "6px",
    lg: "8px",
    xl: "12px",
    "2xl": "16px",
    "3xl": "24px",
    full: "9999px",
  },
  columns: {
    auto: "auto",
    1: "1",
    2: "2",
    3: "3",
    4: "4",
    5: "5",
    6: "6",
    7: "7",
    8: "8",
    9: "9",
    10: "10",
    11: "11",
    12: "12",
    "3xs": "256px",
    "2xs": "288px",
    xs: "320px",
    sm: "384px",
    md: "448px",
    lg: "512px",
    xl: "576px",
    "2xl": "672px",
    "3xl": "768px",
    "4xl": "896px",
    "5xl": "1024px",
    "6xl": "1152px",
    "7xl": "1280px",
  },
  fontSize: {
    xs: ["12px", { lineHeight: "16px" }],
    sm: ["14px", { lineHeight: "20px" }],
    base: ["16px", { lineHeight: "24px" }],
    lg: ["18px", { lineHeight: "28px" }],
    xl: ["20px", { lineHeight: "28px" }],
    "2xl": ["24px", { lineHeight: "32px" }],
    "3xl": ["30px", { lineHeight: "36px" }],
    "4xl": ["36px", { lineHeight: "36px" }],
    "5xl": ["48px", { lineHeight: "1" }],
    "6xl": ["60px", { lineHeight: "1" }],
    "7xl": ["72px", { lineHeight: "1" }],
    "8xl": ["96px", { lineHeight: "1" }],
    "9xl": ["144px", { lineHeight: "1" }],
  },
  lineHeight: {
    none: "1",
    tight: "1.25",
    snug: "1.375",
    normal: "1.5",
    relaxed: "1.625",
    loose: "2",
    3: "12px",
    4: "16px",
    5: "20px",
    6: "24px",
    7: "28px",
    8: "32px",
    9: "36px",
    10: "40px",
  },
  maxWidth: ({ theme, breakpoints }) => ({
    none: "none",
    0: "0px",
    xs: "320px",
    sm: "384px",
    md: "448px",
    lg: "512px",
    xl: "576px",
    "2xl": "672px",
    "3xl": "768px",
    "4xl": "896px",
    "5xl": "1024px",
    "6xl": "1152px",
    "7xl": "1280px",
    full: "100%",
    min: "min-content",
    max: "max-content",
    fit: "fit-content",
    prose: "65ch",
    ...breakpoints(theme("screens")),
  }),
  spacing: {
    px: "1px",
    0: "0",
    0.5: "2px",
    1: "4px",
    1.5: "6px",
    2: "8px",
    2.5: "10px",
    3: "12px",
    3.5: "14px",
    4: "16px",
    5: "20px",
    6: "24px",
    7: "28px",
    8: "32px",
    9: "36px",
    10: "40px",
    11: "44px",
    12: "48px",
    14: "56px",
    16: "64px",
    20: "80px",
    24: "96px",
    28: "112px",
    32: "128px",
    36: "144px",
    40: "160px",
    44: "176px",
    48: "192px",
    52: "208px",
    56: "224px",
    60: "240px",
    64: "256px",
    72: "288px",
    80: "320px",
    96: "384px",
  },
}

@PooSham
Copy link

PooSham commented May 14, 2022

@joshua-scott I created a function that recursively looks after values that are formatted like float numbers and ending with "rem", and converts them to px.

function rem2px(input, fontSize = 16) {
  if (input == null) {
    return input;
  }
  switch (typeof input) {
    case "object":
      if (Array.isArray(input)) {
        return input.map((val) => rem2px(val, fontSize));
      } else {
        const ret = {};
        for (const key in input) {
          ret[key] = rem2px(input[key]);
        }
        return ret;
      }
    case "string":
      return input.replace(
        /(\d*\.?\d+)rem$/,
        (_, val) => parseFloat(val) * fontSize + "px"
      );
    default:
      return input;
  }
}

You can then call it with the default theme by importing it:

const defaultTheme = require("tailwindcss/defaultTheme");

And my theme configuration looks something like this:

...
borderRadius: rem2px(defaultTheme.borderRadius),
columns: rem2px(defaultTheme.columns),
fontSize: rem2px(defaultTheme.fontSize),
lineHeight: rem2px(defaultTheme.lineHeight),
minHeight: {
  ...rem2px(defaultTheme.minHeight),
  4: "16px",
  8: "32px"
},
...

@rikgirbes
Copy link

Awesome @PooSham, although this really should be in the core config as "unit" or such... Don't you think? This feels kinda hackish....

@PooSham
Copy link

PooSham commented May 18, 2022

Awesome @PooSham, although this really should be in the core config as "unit" or such... Don't you think? This feels kinda hackish....

Oh absolutely, but the tailwind devs don't seem too keen on implementing that, so I guess we'll have to live with hacks until they change their mind.

@joshua-scott
Copy link

@PooSham awesome! Using your function I simplified my tailwind config to this:

theme: {
  /**
   * Default tailwind values converted from rem to px:
   **/
  borderRadius: rem2px(defaultTheme.borderRadius),
  columns: rem2px(defaultTheme.columns),
  fontSize: rem2px(defaultTheme.fontSize),
  lineHeight: rem2px(defaultTheme.lineHeight),
  maxWidth: ({ theme, breakpoints }) => ({
    ...rem2px(defaultTheme.maxWidth({ theme, breakpoints })),
  }),
  spacing: rem2px(defaultTheme.spacing),
}

@ineptian
Copy link

ineptian commented Jun 9, 2022

@PooSham I am a little confused about the first and second steps in your solution. Where do you store this function and where do you call it?

@PooSham
Copy link

PooSham commented Jun 9, 2022

@ineptian Sorry I wasn't clear, the second step doesn't call the function, it just imports the default configuration so that the function can be called with it later on in step 3. I have the function stored in tailwind.conf.js, before I export the configuration. The "require" line is in the beginning of the file.

Here's a complete tailwind.conf.js:

const defaultTheme = require("tailwindcss/defaultTheme");

function rem2px(input, fontSize = 16) {
  if (input == null) {
    return input;
  }
  switch (typeof input) {
    case "object":
      if (Array.isArray(input)) {
        return input.map((val) => rem2px(val, fontSize));
      } else {
        const ret = {};
        for (const key in input) {
          ret[key] = rem2px(input[key]);
        }
        return ret;
      }
    case "string":
      return input.replace(
        /(\d*\.?\d+)rem$/,
        (_, val) => parseFloat(val) * fontSize + "px"
      );
    default:
      return input;
  }
}

module.exports = {
  content: ["src/**/*.{js,jsx,ts,tsx}"],
  darkMode: "media",
  theme: {
    borderRadius: rem2px(defaultTheme.borderRadius),
    columns: rem2px(defaultTheme.columns),
    fontSize: rem2px(defaultTheme.fontSize),
    lineHeight: rem2px(defaultTheme.lineHeight),
    minHeight: {
        ...rem2px(defaultTheme.minHeight),
        4: "16px",
        8: "32px"
    },
  }
};

@ineptian
Copy link

ineptian commented Jun 9, 2022

@PooSham Thanks. Got this working. This is slick. I do a lot of landing pages for random clients and sometimes their native REMS are whacky, so this helps a ton.

In case anyone else that is illiterate like me needs clarification:

Paste the following above module.exports = { in your tailwind.config.js

const defaultTheme = require("tailwindcss/defaultTheme");

function rem2px(input, fontSize = 16) {
  if (input == null) {
    return input;
  }
  switch (typeof input) {
    case "object":
      if (Array.isArray(input)) {
        return input.map((val) => rem2px(val, fontSize));
      } else {
        const ret = {};
        for (const key in input) {
          ret[key] = rem2px(input[key]);
        }
        return ret;
      }
    case "string":
      return input.replace(
        /(\d*\.?\d+)rem$/,
        (_, val) => parseFloat(val) * fontSize + "px"
      );
    default:
      return input;
  }
}

And then within the area where you define all your tailwind theme settings theme {} add:

borderRadius: rem2px(defaultTheme.borderRadius),
    columns: rem2px(defaultTheme.columns),
    fontSize: rem2px(defaultTheme.fontSize),
    lineHeight: rem2px(defaultTheme.lineHeight),
    maxWidth: ({ theme, breakpoints }) => ({
      ...rem2px(defaultTheme.maxWidth({ theme, breakpoints })),
    }),
    minHeight: {
        ...rem2px(defaultTheme.minHeight),
        4: "16px",
        8: "32px"
    },

I also agree that this should be native within tailwind. Would make using it even that much easier. Should be as simple as setting "rem" or "px" in the config I feel like.

@joshua-scott How did you figure out how to implement this function with max-width like that? That is also super helpful, I took a shot at it myself before I saw that you had already figured it out. I am just wondering why it needs to be formatted differently than the others.

Thanks to you both!

@LookRain
Copy link

LookRain commented Jun 9, 2022

Hi I know this is an old thread, but I think I figured out a better way to do this with postcss (assuming you are using postcss and want to apply the rem->px conversion across your project)

Just install this postcss plugin and add it in your postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
   '@thedutchcoder/postcss-rem-to-px': {}, // you can add option like the base font size
    autoprefixer: {},
  },
};

@kevinmu17
Copy link

kevinmu17 commented Jul 11, 2022

With big thanks to @PooSham
For people who want to stick with the REM like me but want to see the pixel units as a hint, here you go
For PHPstorm users: Preferences > Editor > General > Code Completion > [X] Show the documentation popup. This will
show the quickdocumentation window next to the autocomplete window. This will help until JetBrains fixes the autocomplete with this info ;)

function rem2px(input, fontSize = 16) {
  if (input == null) {
    return input;
  }
  switch (typeof input) {
    case "object":
      if (Array.isArray(input)) {
        return input.map((val) => rem2px(val, fontSize));
      } else {
        const ret = {};
        for (const key in input) {
          ret[key] = rem2px(input[key]);
        }
        return ret;
      }
    case "string":
      return input.replace(
        /(\d*\.?\d+)rem$/,
        (_, val) => `${input} /** ${parseFloat(val) * fontSize}px */`
      );
    default:
      return input;
  }
}

@PooSham
Copy link

PooSham commented Jul 11, 2022

Hi I know this is an old thread, but I think I figured out a better way to do this with postcss (assuming you are using postcss and want to apply the rem->px conversion across your project)

Just install this postcss plugin and add it in your postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
   '@thedutchcoder/postcss-rem-to-px': {}, // you can add option like the base font size
    autoprefixer: {},
  },
};

I used this before I created the tailwind config function. It works ok, but it's another dependency on a project which is barely updated. I preferred to stay in control.

@PooSham
Copy link

PooSham commented Jul 11, 2022

With big thanks to @PooSham For people who want to stick with the REM like me but want to see the pixel units as a hint, here you go

function rem2px(input, basePixel = 16) {
    // console.log(input);
    if (input == null) {
        return input;
    }
    switch (typeof input) {
        case "object":
            if (Array.isArray(input)) {
                return input.map((val) => rem2px('\n' +val, basePixel))
            } else {
                const ret = {}
                for (const key in input) {
                    if (Array.isArray(input[key])) {
                        let item = []
                        input[key].map((val) => {
                            switch (typeof val) {
                                case "object":
                                    const lineHeightObject = {}
                                    for (const key in val) {
                                        item.push(val[key] + ' /**' + rem2px(val[key]) + '*/')
                                    }
                                case "string":
                                    item.push(val + ' /**' + rem2px(val) + '*/')
                            }
                            ret[key] = item
                        })
                    } else {
                        ret[key] = input[key] + ' /**' + rem2px(input[key]) + '*/'
                    }
                }
                return ret
            }
        case "string":
            return input.replace(
                /(\d*\.?\d+)rem$/,
                (_, val) => parseFloat(val) * basePixel + "px"
            );
        default:
            return input;
    }
}

Made this solution pure as temporary patch for phpStorm

That's not a bad idea, but this solution seems unnecessarily complex and actually incomplete. You should just update the "string" case, like this:

function rem2px(input, fontSize = 16) {
  if (input == null) {
    return input;
  }
  switch (typeof input) {
    case "object":
      if (Array.isArray(input)) {
        return input.map((val) => rem2px(val, fontSize));
      } else {
        const ret = {};
        for (const key in input) {
          ret[key] = rem2px(input[key]);
        }
        return ret;
      }
    case "string":
      return input.replace(
        /(\d*\.?\d+)rem$/,
        (_, val) => `${input} /** ${parseFloat(val) * fontSize}px */`
      );
    default:
      return input;
  }
}

@kevinmu17
Copy link

@PooSham wow, something went terribly wrong with my debugging... somehow it got me confused with the fontSize object > array > object construction... think my docker cashed something there and messed up my conclusion. Thanks for the clarification, it works like a charm and you saved JetBrains a refund hahaha, i'll remove my code to not confuse anyone

@fweth
Copy link

fweth commented Nov 2, 2022

What if you want to use px for margins, paddings, gaps, etc. but rem for width and height? Then customizing spacing wouldn't work? Here is a good article on the topic:

https://www.joshwcomeau.com/css/surprising-truth-about-pixels-and-accessibility/

I think rem should be used for positive space and px for negative space. So if somebody changes their base font size, it shouldn't crank up all the white space on the page, but make the layout denser, in order to still fit a good amount on content in the window.

@abrehamgezahegn
Copy link

abrehamgezahegn commented Nov 15, 2022

For anyone who is still looking for a workaround. I used postcss-rem-to-pixel to convert the output css from rem to px

remToPixel.js

var fs = require("fs");
var postcss = require("postcss");
var remToPx = require("postcss-rem-to-pixel");

const replaceRem = (filePath) => {
  var css = fs.readFileSync(filePath, "utf8");
  var options = {
    replace: false,
    rootValue: 16,
    unitPrecision: 5,
    propList: ["*"],
    selectorBlackList: [],
    replace: true,
    mediaQuery: false,
    minRemValue: 0,
  };
  var processedCss = postcss(remToPx(options)).process(css).css;

  fs.writeFile(filePath, processedCss, function (err) {
    if (err) {
      throw err;
    }
    console.log("Rem file written.");
  });
};

replaceRem("./src/output.css");

package.json

{ 
  ...
  "scripts": {
     ...
     "convertRemToPixel": "nodemon src/utils/remToPixel.js"
    ...
  },
}

@HullQin
Copy link

HullQin commented Nov 29, 2022

Thank @PooSham . And I do some other work on that:

  • Transfer rem2px on function configs, which can transfer maxWidth correctly. (Line 22)
  • Add fontSize argument when transferring rem2px on object. (Line 14)
const defaultTheme = require('tailwindcss/defaultTheme');

function rem2px(input, fontSize = 16) {
  if (input == null) {
    return input;
  }
  switch (typeof input) {
    case 'object':
      if (Array.isArray(input)) {
        return input.map((val) => rem2px(val, fontSize));
      }
      const ret = {};
      for (const key in input) {
        ret[key] = rem2px(input[key], fontSize);
      }
      return ret;
    case 'string':
      return input.replace(
        /(\d*\.?\d+)rem$/,
        (_, val) => `${parseFloat(val) * fontSize}px`,
      );
    case 'function':
      return eval(input.toString().replace(
        /(\d*\.?\d+)rem/g,
        (_, val) => `${parseFloat(val) * fontSize}px`,
      ));
    default:
      return input;
  }
}

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['src/**/*.{js,jsx,ts,tsx}'],
  theme: rem2px(defaultTheme),
};

@kalnode
Copy link

kalnode commented Jan 1, 2023

This is a problem for me too. I ended up using a PostCSS plugin to convert rem-to-px automatically. I don't want to copy-paste things into tw config nor have to make choices on specific elements. The plugin I can toggle on/off.

Use case: a Chrome Extension that injects a small floating UI (box with controls) onto every webpage visited, and I use TW to style it. Webpages in the wild all have crazy base styles that affect the box's UI. If I do nothing, then on one page the box will appear normal, and another webpage it'll be micro sized because someone thought setting font-size: 10px on body was a good idea.

@GorvGoyl
Copy link

I'm using twind.style to inject tailwind to shadow dom and managed to convert rem to px https://gourav.io/blog/tailwind-in-shadow-dom#configure-twind

@baptisteArno
Copy link

Thank guys. 🙏

I needed this because I'm implementing a widget library with Shadow DOM. Having the sizing in px is mandatory, because the widget will be added to any website

@longzheng
Copy link

Hi I know this is an old thread, but I think I figured out a better way to do this with postcss (assuming you are using postcss and want to apply the rem->px conversion across your project)

Just install this postcss plugin and add it in your postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
   '@thedutchcoder/postcss-rem-to-px': {}, // you can add option like the base font size
    autoprefixer: {},
  },
};

This is exactly what I needed.

Just in case anyone else is also confused, the plugin README has the incorrect name for the install instructions. You'll need

npm install @thedutchcoder/postcss-rem-to-px

@mhmo91
Copy link

mhmo91 commented Jun 26, 2023

You can configure Tailwind to use whatever units you want, just open up your tailwind.config.js file and and go to town.

https://tailwindcss.com/docs/theme

We can only provide one default configuration and the default configuration we've chosen to provide uses rem to size things. If you want to use pixels you can provide your own pixel-based configuration.

That's not really an answer good enough. it lacks in it's base any empathy to the use case mentioned

@pgmichael
Copy link

pgmichael commented Jul 25, 2023

Adding my +1 to re-open this! It would be nice to have a single option that would change all values rather than having to do it manually, add further dependencies or use a function inside tailwind.config.js which makes customizing further values harder.

I am developing an extension that injects a React app into the current tab of the user. To prevent any potential conflicts with existing styles, I have contained my app within a shadow root. However, given that rem values are inherently derived from the base html element, this causes inconsistencies in style rendering (ex: on one page text-sm could be 7px and on the other 12px).

Right now I'm fetching the text content of my stylesheet and using a regex to convert all rem values to px. It works.. but it's quite hacky!

// Fetch the CSS file
fetch(chrome.runtime.getURL('assets/styles.css'))
  .then((response) => {
    if (!response.ok) {
      throw new Error('Network response was not ok')
    }
    return response.text()
  })
  .then((text) => {
    // Parse CSS text and convert rem values to px
    const parsedText = text.replace(/(\d*\.?\d+)rem/g, (match, group) => {
      // Convert rem to px based on base size 16px
      const pxValue = parseFloat(group) * 16
      return `${pxValue}px`
    })

    // Create a style element for the CSS file
    const styleEl = document.createElement('style')
    styleEl.textContent = parsedText
    shadowRoot.appendChild(styleEl)

    document.body.appendChild(root)
    createRoot(shadowRoot).render(<ContentScript />)
  })
  .catch((error) => {
    console.error('Failed to load CSS: ', error)
  })

@JohnCido
Copy link

嗨,我知道这是一个旧线程,但我认为我找到了一种更好的方法来使用 postcss 来做到这一点(假设您正在使用 postcss 并希望在您的项目中应用 rem->px 转换)
只需安装这个postcss 插件并将其添加到您的postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
   '@thedutchcoder/postcss-rem-to-px': {}, // you can add option like the base font size
    autoprefixer: {},
  },
};

这正是我所需要的。

为了防止其他人也感到困惑,插件自述文件的安装说明名称不正确。你需要

npm install @thedutchcoder/postcss-rem-to-px

This is the most simple way to achieve quick rem to px convertion.

@jmiller-rise8
Copy link

https://www.npmjs.com/package/@rise8/tailwind-pixel-perfect-preset

@cumber
Copy link

cumber commented Aug 22, 2023

The trouble is Tailwind's default config doesn't entirely use rem. It mostly does, but then it uses px for border width, shadows, underline offsets, etc. And of course there's the px size for 1px spacing etc.

The net result is that most things will scale with the root font size, but some things won't. So this setup denies the user the ability to change their default text size independently from the scale of the entire design (e.g. to enlarge text for accessibility reasons), but it also fails to make the root font scale the site's design. Instead Tailwind designs effectively rely on the root font size at runtime being pretty close to a fixed value the designer had in mind, despite that value actually being dependent on the browser and the user's settings.

The claimed advantage of using rem everywhere is that it makes the site's design automatically scale when the root font size is adjusted. But adjusting the default font size is a really weird way to scale the design (rather than the text) anyway. If the user wants to do that, they'll use the zoom settings in the browser. If the designer wants to do that (e.g. to make things bigger on large screens), that's what transform: scale is for. Using rem everywhere is unnecessary for making the site scalable, and using rem nearly everywhere makes it scale worse than just using px everywhere.

@SentretC
Copy link

SentretC commented Sep 11, 2023

On desktop it might be good enough to scale everything together (but I don't think developers should do that - users can set a system-wide scaling if they prefer everything larger generally, or a site-specific one of course). However on mobile it can be a waste of screen space to also scale up spacing when the user just needs larger texts.

I would prefer having not just one, but separate options for different kinds of sizes.

@maxpatiiuk
Copy link

I was in a situation where I needed to make Tailwind use em instead of rem (see #3105 (comment)). My solution should work for your case too with a modification:

Here is a tiny PostCSS plugin, that replaces all occurrences of "rem" with "em" in the output CSS files:
const convertRemToEm = {
  postcssPlugin: 'convertRemToEm',
  // When debugging this, https://astexplorer.net/#/2uBU1BLuJ1 is very helpful
  Declaration(declaration) {
    declaration.value = declaration.value.replaceAll(remRegex, 'em');
  },
};

// Regex to find all occurrences of "rem" units
const remRegex = /(?<=\d)rem/g;

/** @type {import('postcss-load-config').Config} */
module.exports = {
  plugins: [
    // ... your other plugins here
    // Add this plugin:
    convertRemToEm,
  ],
};

If you want to use px instead, it might look something like this:

const convertRemToEm = {
  postcssPlugin: 'convertRemToEm',
  // When debugging this, https://astexplorer.net/#/2uBU1BLuJ1 is very helpful
  Declaration(declaration) {
    declaration.value = declaration.value.replaceAll(
      remRegex,
      (_, number) => `${Number.parseInt(number) * 16}px`,
    );
  },
};

// Regex to find all occurrences of "rem" units and capture the number
const remRegex = /(\d+\.?\d+)rem/g;

/** @type {import('postcss-load-config').Config} */
module.exports = {
  plugins: [
    // ... your other plugins here
    // Add this plugin:
    convertRemToEm,
  ],
};

@LeoRedin
Copy link

LeoRedin commented Nov 1, 2023

I was in a situation where I needed to make Tailwind use em instead of rem (see #3105 (comment)). My solution should work for your case too with a modification:

Here is a tiny PostCSS plugin, that replaces all occurrences of "rem" with "em" in the output CSS files:

const convertRemToEm = {
  postcssPlugin: 'convertRemToEm',
  // When debugging this, https://astexplorer.net/#/2uBU1BLuJ1 is very helpful
  Declaration(declaration) {
    declaration.value = declaration.value.replaceAll(remRegex, 'em');
  },
};

// Regex to find all occurrences of "rem" units
const remRegex = /(?<=\d)rem/g;

/** @type {import('postcss-load-config').Config} */
module.exports = {
  plugins: [
    // ... your other plugins here
    // Add this plugin:
    convertRemToEm,
  ],
};

If you want to use px instead, it might look something like this:

const convertRemToEm = {
  postcssPlugin: 'convertRemToEm',
  // When debugging this, https://astexplorer.net/#/2uBU1BLuJ1 is very helpful
  Declaration(declaration) {
    declaration.value = declaration.value.replaceAll(
      remRegex,
      (_, number) => `${Number.parseInt(number) * 16}px`,
    );
  },
};

// Regex to find all occurrences of "rem" units and capture the number
const remRegex = /(\d+\.?\d+)rem/g;

/** @type {import('postcss-load-config').Config} */
module.exports = {
  plugins: [
    // ... your other plugins here
    // Add this plugin:
    convertRemToEm,
  ],
};

Hello! I'm facing the same issue, Im creating a web component and then I got into the same problem. Here's a sandbox in case you have some time, I cannot seem to bypass the html font sizing. This is a simpler example but if I get that going the rest should be fine

@lucasdellabella
Copy link

Just install this postcss plugin and add it in your postcss.config.js

Reporting in - @TheDutchCoder's postcss plugin is the way to go.

@TimonPeng
Copy link

TimonPeng commented Apr 18, 2024

Resize rem:

function remResize(input, ratio = 1) {
  if (input == null) {
    return input;
  }
  switch (typeof input) {
    case 'object':
      if (Array.isArray(input)) {
        return input.map((val) => remResize(val, ratio));
      }
      const ret = {};
      for (const key in input) {
        ret[key] = remResize(input[key], ratio);
      }
      return ret;
    case 'string':
      return input.replace(/(\d*\.?\d+)rem$/, (_, val) => `${parseFloat(val) / ratio}rem`);
    case 'function':
      return eval(input.toString().replace(/(\d*\.?\d+)rem/g, (_, val) => `${parseFloat(val) / ratio}rem`));
    default:
      return input;
  }
}
module.exports = {
  theme: {
    ...remResize(defaultTheme, 0.625),
    colors: {},
  },
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests