Skip to content

Instantly share code, notes, and snippets.

@stevedya
Created May 2, 2025 23:08
Show Gist options
  • Save stevedya/3123b4555c530a009a5fbcf550cc116a to your computer and use it in GitHub Desktop.
Save stevedya/3123b4555c530a009a5fbcf550cc116a to your computer and use it in GitHub Desktop.
Tailwind 3.4 responsive spacing plugin
/**
* Responsive Spacing Plugin
*
* This plugin generates responsive spacing utility classes for Tailwind CSS based on spacing tokens in the config.
* It creates utility classes for padding, margin, and gap with values that adjust based on screen sizes.
*
* How it works:
* - Accepts spacing tokens, which can be a single value or an object representing values for different breakpoints (e.g., 'sm', 'md', 'lg').
* - Generates utility classes for different directions (e.g., padding-left, margin-top) that adapt to the screen size using Tailwind's responsive breakpoints.
*
* Example classes: .p-scale-sm-4, .m-scale-md-2, .gap-scale-lg the generated classes, simply add them to your HTML elements in the same way you would use any other Tailwind CSS utility class.
*
* @example
* <div class="pt-scale-sm px-scale-lg gap-scale-lg bg-surface">
* <!-- Your content here -->
* </div>
*
* @example plugin usage inside of tailwind.config.js:
* responsiveSpacingPlugin({
* addComponents: function() { ... },
* tokens: {
* 'scale-0': {
* sm: '1rem',
* md: '2rem',
* lg: '3rem',
* },
* 'scale-1': theme('spacing.1'), // Singe values can be used to set all breakpoints at once
* // ... more scales
* }
* });
*
* @param {Function} options.addComponents - The function provided by Tailwind CSS to register new CSS classes.
* @param {Object} options.tokens - The tokens object that represents different spacing scales. Each key-value pair in the object represents a different spacing scale. Each scale is an object itself, with keys for small, medium, and large screens, and values that are the spacing values for each screen size.
*
*/
module.exports = function responsiveSpacingPlugin({ addComponents, tokens }) {
// Helper function to generate directional classes with responsive breakpoints
const generateDirectionalClasses = (
prefix,
direction,
properties,
themeKey,
breakpoints,
) => {
// If a single value is provided, use it for all screen sizes
const bps =
typeof breakpoints === 'string' || typeof breakpoints === 'number'
? { sm: breakpoints, md: breakpoints, lg: breakpoints }
: breakpoints;
const className = `.${prefix}${direction}-${themeKey}`;
// Initialize the styles object with all directions and combine them
const styles = {
[className]: {},
};
// For the small breakpoint (or default), combine all properties
properties.forEach((property) => {
if (bps.sm) {
styles[className][property] = bps.sm;
}
});
// Combine properties for the medium breakpoint
if (bps.md) {
styles[className]['@screen md'] = {};
properties.forEach((property) => {
styles[className]['@screen md'][property] = bps.md;
});
}
// Combine properties for the large breakpoint
if (bps.lg) {
styles[className]['@screen lg'] = {};
properties.forEach((property) => {
styles[className]['@screen lg'][property] = bps.lg;
});
}
return styles;
};
// This function generates classes for padding and margin based on the token set that's defined in tailwind.config.js for the responsiveSpacingPlugin.
// in the `directions` array. The generated classes will be a responsive based on the values supplied in the spacing object.
const generateClasses = (prefix, tokensList, directions) => {
const classes = {};
Object.entries(tokensList).forEach(([themeKey, breakpoints]) => {
directions.forEach(({ direction, properties }) => {
Object.assign(
classes,
generateDirectionalClasses(
prefix,
direction,
properties,
themeKey,
breakpoints,
),
);
});
});
return classes;
};
// Generate classes for margin
const marginDirections = [
{ direction: '', properties: ['margin'] }, // Full margin
{ direction: 'x', properties: [`margin-left`, `margin-right`] }, // Left/Right margin
{ direction: 'y', properties: [`margin-top`, `margin-bottom`] }, // Top/Bottom margin
{ direction: 't', properties: [`margin-top`] }, // Top margin
{ direction: 'b', properties: [`margin-bottom`] }, // Bottom margin
{ direction: 'l', properties: [`margin-left`] }, // Left margin
{ direction: 'r', properties: [`margin-right`] }, // Right margin
];
const marginClasses = generateClasses('m', tokens, marginDirections);
// Generate classes for padding
const paddingDirections = [
{ direction: '', properties: ['padding'] }, // Full padding
{ direction: 'x', properties: [`padding-left`, `padding-right`] }, // Left/Right padding
{ direction: 'y', properties: [`padding-top`, `padding-bottom`] }, // Top/Bottom padding
{ direction: 't', properties: [`padding-top`] }, // Top padding
{ direction: 'b', properties: [`padding-bottom`] }, // Bottom padding
{ direction: 'l', properties: [`padding-left`] }, // Left padding
{ direction: 'r', properties: [`padding-right`] }, // Right padding
];
const paddingClasses = generateClasses('p', tokens, paddingDirections);
// Logical properties for padding
const logicalPaddingDirections = [
{ direction: 's', properties: ['padding-inline-start'] }, // Start padding
{ direction: 'e', properties: ['padding-inline-end'] }, // End padding
];
const logicalPaddingClasses = generateClasses(
'p',
tokens,
logicalPaddingDirections,
);
// Logical properties for margin
const logicalMarginDirections = [
{ direction: 's', properties: ['margin-inline-start'] }, // Start margin
{ direction: 'e', properties: ['margin-inline-end'] }, // End margin
];
const logicalMarginClasses = generateClasses(
'm',
tokens,
logicalMarginDirections,
);
// Gap directions for gap:, column-gap:, row-gap:
const gapDirections = [
{ direction: '', properties: ['gap'] }, // Full gap
{ direction: '-x', properties: ['column-gap'] }, // Column gap
{ direction: '-y', properties: ['row-gap'] }, // Row
];
const gapClasses = generateClasses('gap', tokens, gapDirections);
// Add the generated classes to Tailwind
addComponents({
...paddingClasses,
...marginClasses,
...logicalPaddingClasses,
...logicalMarginClasses,
...gapClasses,
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment