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

Add new min and max variants #9558

Merged
merged 9 commits into from Oct 14, 2022
Merged

Add new min and max variants #9558

merged 9 commits into from Oct 14, 2022

Conversation

thecrypticace
Copy link
Contributor

@thecrypticace thecrypticace commented Oct 14, 2022

This PR introduces three new pieces of functionality related to screen variants.

Note These features all require a "simple" screens config. Please see the restrictions below on when they are enabled and what you can and can't do.

New Features

Sort screens by value (instead of config order)

Currently, screens are sorted by the order they appear in your tailwind.config.js file. This is great until you want to add your own xs variant through extends. Right now if you do that Tailwind will place the xs variant at the end of your screens list and at the end of your CSS, causing xs to incorrectly take precedence over higher screen variants / sizes.

To work around this you must duplicate your screens list and place xs at the beginning:

module.exports = {
  theme: {
    screens: {
      xs: "320px",
      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
    },
  },
};

Now, with this change, you can use extends and it will work as expected:

module.exports = {
  theme: {
    extend: {
      screens: {
        xs: "320px",
      },
    },
  },
};

With the above config and this HTML:

<div class="xs:px-2 sm:px-4"></div>

This is now the output:

@media (min-width: 320px) {
  .xs\:px-2 {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
}

@media (min-width: 640px) {
  .sm\:px-4 {
    padding-left: 1rem;
    padding-right: 1rem;
  }
}

Adds a max-* variant for max-width screen sizes

Currently, it is possible to define your own max-width screen sizes in the config by adding more screens in the appropriate order:

module.exports = {
  theme: {
    screens: {
      "max-2xl": { max: "1536px" },
      "max-xl": { max: "1280px" },
      "max-lg": { max: "1024px" },
      "max-md": { max: "768px" },
      "max-sm": { max: "640px" },

      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
    },
  },
};

But this solution has some drawbacks:

  1. These new max-width screens MUST appear in descending order. Not doing so would cause max-2xl to override the all the other max-width screens since screens that are 480px wide, 768px wide, etc… are all also at most 1536px wide.
  2. You can't use extends to add them because they should be placed before min-width screens for optimal behavior.
  3. Because of the above, you have to duplicate your entire screen list.

This new feature addresses all of these shortcomings for simple, min-width-based screen configs.

Better yet, you do not have to add more screens yourself — they're automatically supported!

Now, without modifying your screens at all you'll be able to write this:

<div class="px-5 max-sm:px-2 sm:px-4"></div>

And this is the output:

.px-5 {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}

@media not all and (min-width: 640px) {
  .max-sm\:px-2 {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
}

@media (min-width: 640px) {
  .sm\:px-4 {
    padding-left: 1rem;
    padding-right: 1rem;
  }
}

Further, you can combine standard screen variants and the new max variant to produce a "between" or range variant for your utility:

<div class="sm:max-md:px-2"></div>
@media (min-width: 640px) {
  @media not all and (min-width: 768px) {
    .sm\:max-md\:px-2 {
      padding-left: 0.5rem;
      padding-right: 0.5rem;
    }
  }
}

Which means, when the viewport is at least 640px and less than 768px, the padding will be applied.

The max-* variant also supports arbitrary values

And, because it utilizes the new matchVariant feature in v3.2+ the max-* variant supports arbitrary values, not just screen names. This is great for one-off cases when it doesn't make sense to add an additional screen to your config. This means you can do things like this:

<div class="max-[320px]:px-2 max-sm:px-4 px-5"></div>

And this is the output:

.px-5 {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}

@media not all and (min-width: 480px) {
  .max-sm\:px-4 {
    padding-left: 1rem;
    padding-right: 1rem;
  }
}

@media not all and (min-width: 320px) {
  .max-\[320px\]\:px-2 {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
}

Adds a min-[] variant specifically for arbitrary values

Tailwind's screens are all already min-width by default. And this holds true for most projects that customize their screens config to add additional screen sizes. This means that, normally, there's no need for a min variant.

But, wait, we just added arbitrary values to the max-* variant. If you can do that for max-width screen sizes it stands to reason that you'd want to do this for min-width screen sizes as well.

Introducing the min-[] variant!

You can now do this:

<div class="min-[320px]:px-2 px-5"></div>

And this is the output:

.px-5 {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}
@media (min-width: 320px) {
  .min-\[320px\]\:px-2 {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
}

Even better, these are sorted in ascending order alongside other screen variants in your project's config since they are also min-width-based:

<div class="min-[320px]:px-2 sm:px-4 px-5"></div>

Which produces this output:

.px-5 {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}
@media (min-width: 320px) {
  .min-\[320px\]\:px-2 {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
}
@media (min-width: 640px) {
  .sm\:px-4 {
    padding-left: 1rem;
    padding-right: 1rem;
  }
}

Note: The min-[] variant is only available for arbitrary values and cannot be used with named screen sizes.

Restrictions

Your screens, if customized, MUST be min-width only and string values

To ensure proper sorting we've made the decision to restrict the use of these features to screens that are "simple", meaning that they are min-width-based, and consist of only string values that use the same units.

For example, these are all valid:

module.exports = {
  theme: {
    screens: {
      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
    },
  },
};
module.exports = {
  theme: {
    extends: {
      screens: {
        xs: "320px",
        reallybig: "3440px",
      },
    },
  },
};

While these are invalid and will disable these new features:

module.exports = {
  theme: {
    screens: {
      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
      portrait: { raw: "(orientation: portrait)" },
    },
  },
};
module.exports = {
  theme: {
    extends: {
      screens: {
        xs: { min: "320px" },
      },
    },
  },
};
module.exports = {
  theme: {
    extends: {
      screens: {
        xs: { max: "320px" },
      },
    },
  },
};
module.exports = {
  theme: {
    extends: {
      screens: {
        xs: { min: "320px", max: "479px" },
      },
    },
  },
};

psst if you're using custom screens in your project for landscape, portrait, or print media queries we have variants for those already!

All screens including both built-in and custom ones MUST all use the same units

For example, these are all valid:

module.exports = {
  theme: {
    screens: {
      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
    },
  },
};
module.exports = {
  theme: {
    screens: {
      sm: "40rem",
      md: "48rem",
      lg: "64rem",
      xl: "80rem",
      "2xl": "96rem",
    },
  },
};

While these are invalid and will disable all of these new features together:

module.exports = {
  theme: {
    screens: {
      sm: "640px",
      md: "48rem", // See how we're using rems here instead of px
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
    },
  },
};
module.exports = {
  theme: {
    extends: {
      screens: {
        md: "48rem", // Screens internally are px-based so we can't use rem's here
      },
    },
  },
};

All uses of min and max MUST use the same units (including built in and custom screens)

To ensure proper sorting we've made the decision to restrict the use of min and max to the same units. This means you can't do any of these — across your whole project:

<div class="min-[320px]:px-2 max-[640rem]:px-4"></div>
<div class="min-[320px]:px-2 min-[48rem]:px-4"></div>
<div class="max-[320px]:px-2 max-[48rem]:px-4"></div>

If you want to use rem for your min and max values you'll need to use rem for all of your screens as well.

tl;dr

As long as your screens config is "simple" (see restrictions above):

  1. You can now add screens via extends and they'll be properly sorted among your other screens.
  2. Adds a new min-[] variant that allows you to use arbitrary values with min-width media queries which are sorted in ascending order alongside your other screens.
  3. Adds a new max-* variant, which supports both your screens config and arbitrary values and are sorted in descending order and all of them are before your min-width screens.

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

Successfully merging this pull request may close these issues.

None yet

1 participant