Skip to content
Corey M Collins edited this page May 10, 2022 · 4 revisions

wd_s integrates Tailwind to help handle sensible, consistent styles without a lot of overhead for engineers.

In order to stay consistent with the theme setup out of the box, we recommend adding Tailwind styles via @apply rules in Sass partials rather than adding Tailwind utility classes to HTML markup.

Either way is a great way to use Tailwind! For our purposes, though, we wanted to settle on one path to reduce overhead when new engineers begin working on a project. Staying in Sass partials consistently, our engineers will know that they don't need to track down a template file or template tag to tweak the styles of a particular element.

If you're interested in learning more about Tailwind, checkout the Tailwind docs which are robust and filled to the brim with details and examples to help you get started.

tailwind.config.js

The Tailwind config file in the theme allows us to create and extend global styles, add utility classes, add components, and much more.

Here's what our tailwind.config.js file looks like:

const plugin = require( 'tailwindcss/plugin' );
const glob = require( 'glob' );

// Get arrays of all of the files.
const topLevelPhpFiles = glob.sync( './*.php' ),
	directoryFiles = [
		'./inc/*.php',
		'./template-parts/*.php',
		'./src/js/**/*.js',
	];

module.exports = {
	content: topLevelPhpFiles.concat( directoryFiles ),
	theme: {
		fontSize: {
			'root-em': '16px',
			'xs': '0.75rem',
			'sm': '0.875rem',
			'base': '1rem',
			'lg': '1.125rem',
			'xl': '1.25rem',
			'2xl': '1.375rem',
			'3xl': '1.5rem',
			'4xl': '1.625rem',
			'5xl': '1.75rem',
			'6xl': '1.875rem',
			'heading-xs': '2rem',
			'heading-sm': '2.125rem',
			'heading-md': '2.375rem',
			'heading-lg': '2.625rem',
			'heading-xl': '2.875rem'
		},
		spacing: {
			px: '1px',
			0: '0',
			1: '0.0625rem',
			2: '0.125rem',
			3: '0.1875rem',
			4: '0.25rem',
			5: '0.3125rem',
			6: '0.375rem',
			8: '0.5rem',
			10: '0.625rem',
			12: '0.75rem',
			16: '1rem',
			20: '1.25rem',
			24: '1.5rem',
			32: '2rem',
			40: '2.5rem',
			48: '3rem',
			56: '3.5rem',
			64: '4rem',
			68: '4.25rem',
			72: '4.5rem',
			76: '4.75rem',
			80: '5rem',
			192: '12rem',
		},
		boxShadow: {
			xs: '0 0 0 0.0625rem rgba(0, 0, 0, 0.05)',
			sm: '0 0.0625rem 0.125rem 0 rgba(0, 0, 0, 0.05)',
			default: '0 0.0625rem 0.1875rem 0 rgba(0, 0, 0, 0.1), 0 0.0625rem 0.125rem 0 rgba(0, 0, 0, 0.06)',
			md: '0 0.25rem 0.375rem -0.0625rem rgba(0, 0, 0, 0.1), 0 0.125rem 0.25rem -0.0625rem rgba(0, 0, 0, 0.06)',
			lg: '0 0.625rem 0.9375 -0.1875rem rgba(0, 0, 0, 0.1), 0 0.25rem 0.375rem -0.125rem rgba(0, 0, 0, 0.05)',
			xl: '0 1.25rem 1.5625rem -0.3125rem rgba(0, 0, 0, 0.1), 0 0.625rem 0.625rem -0.3125rem rgba(0, 0, 0, 0.04)',
			'2xl': '0 1.5625rem 3.125rem -10.125rem rgba(0, 0, 0, 0.25)',
			'3xl': '0 2.1875rem 3.75rem -0.9375rem rgba(0, 0, 0, 0.3)',
			inner: 'inset 0 0.125rem 0.25rem 0 rgba(0, 0, 0, 0.06)',
			outline: '0 0 0 0.1875rem rgba(66, 153, 225, 0.5)',
			focus: '0 0 0 0.1875rem rgba(66, 153, 225, 0.5)',
			none: 'none',
		},
		screens: {
			'phone': '300px',
			'tablet-portrait': '600px',
			'wp-admin-bar': '783px',
			'tablet-landscape': '900px',
			'desktop-min': {'min': '1200px'},
			'desktop': '1200px',
			'desktop-large': '1600px',
		},
		container: {
			center: true,
			screens: {
				'phone': '100%',
				'desktop': '1200px',
			},
		},
		extend: {
			backgroundOpacity: {
				'10': '0.1',
			},
		},
	},
	future: {
		purgeLayersByDefault: true,
	},
	variants: {},
	plugins: [
		plugin( function({ addBase, config }) {
			addBase({
				'h1,.h1': {
					fontSize: config( 'theme.fontSize.heading-xl' ),
				},
				'h2,.h2': {
					fontSize: config( 'theme.fontSize.heading-lg' ),
				},
				'h3,.h3': {
					fontSize: config( 'theme.fontSize.heading-md' ),
				},
				'h4,.h4': {
					fontSize: config( 'theme.fontSize.heading-sm' ),
				},
				'h5,.h5': {
					fontSize: config( 'theme.fontSize.heading-xs' ),
				},
				'h6,.h6': {
					fontSize: config( 'theme.fontSize.heading-xs' ),
				},
				'h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6': {
					marginBottom: config( 'theme.spacing.16' ),
				},
				'a': {
					textDecoration: 'underline',
				},
				'p': {
					marginBottom: config( 'theme.spacing.16' ),
					'&:last-child': {
						marginBottom: '0',
					},
				},
			})
		}),
		plugin( function({ addComponents, config }) {
			const screenReaderText = {
				'.screen-reader-text': {
					clip: 'rect(1px, 1px, 1px, 1px)',
					height: '1px',
					overflow: 'hidden',
					position: 'absolute',
					whiteSpace: 'nowrap',
					width: '1px',
					'&:hover,&:active,&:focus': {
						backgroundColor: config( 'theme.colors.black' ),
						clip: 'auto',
						color: config( 'theme.colors.white' ),
						display: 'block',
						fontSize: config( 'theme.fontSize.base' ),
						fontWeight: config( 'theme.fontWeight.medium' ),
						height: 'auto',
						left: '5px',
						lineHeight: 'normal',
						padding: config( 'theme.spacing.8' ),
						textDecoration: 'none',
						top: '5px',
						width: 'auto',
						zIndex: '100000',
					},
				},
			}

			addComponents( screenReaderText, {
				variants: ['hover', 'active', 'focus'],
			})
		}),
	],
}

Let's break it down into sections.

Purge

We want to ensure that our compiled stylesheet is as trim as possible. This section of the config file tells Tailwind where to look for potential usage of Tailwind classes. Anything found in any PHP file in our theme or in a JS file in our /src/js/components/ directory will be included in our compiled stylesheet.

For example, Tailwind includes a number of preset color values out of the box. However, unless we include something like bg-red-700 as a class on an element in one of our noted PHP or JS files, the styles for that particular class will not be compiled into our stylesheet.

This will keep only the styles necessary for our theme to function to be included in our compiled stylesheet.

We're also telling Tailwind to purge its utilities layer which will also trim anything not used out of our compiled stylesheet.

Usage:

purge: {
	content: [
		'./**/*.php',
		'./src/components/*.js',
	],
	layers: ['utilities'],
	mode: 'layers',
},

Theme Override: Font Size

By default, Tailwind sets font sizes. However, the sizes Tailwind uses aren't set on the 16px/1rem system we've used in the past.

Here, we're replacing Tailwind's default font size values while also adding a few custom font sizes for use with headings throughout the site.

Usage:

fontSize: {
	'root-em': '16px',
	'xs': '0.75rem',
	'sm': '0.875rem',
	'base': '1rem',
	'lg': '1.125rem',
	'xl': '1.25rem',
	'2xl': '1.375rem',
	'3xl': '1.5rem',
	'4xl': '1.625rem',
	'5xl': '1.75rem',
	'6xl': '1.875rem',
	'heading-xs': '2rem',
	'heading-sm': '2.125rem',
	'heading-md': '2.375rem',
	'heading-lg': '2.625rem',
	'heading-xl': '2.875rem'
},

Theme Override: Spacing

Similar to the font size change, we're overriding Taliwind's default spacing values to use a 16px/1rem base.

This helps cut down on the amount of math you need to do in your head to remember what each value means.

For example, instead of remembering that the default Tailwind spacing of mt-12 creates a margin-top value of 3rem or 48px, our change means that mt-12 creates a margin-top value of 0.75rem or 12px.

All of the numeric values in our custom spacing equal the actual pixel value, which can then be divided by our base 16 to find the rem value. Easy peasy!

Usage:

spacing: {
	px: '1px',
	0: '0',
	1: '0.0625rem',
	2: '0.125rem',
	3: '0.1875rem',
	4: '0.25rem',
	5: '0.3125rem',
	6: '0.375rem',
	8: '0.5rem',
	10: '0.625rem',
	12: '0.75rem',
	16: '1rem',
	20: '1.25rem',
	24: '1.5rem',
	32: '2rem',
	40: '2.5rem',
	48: '3rem',
	56: '3.5rem',
	64: '4rem',
	68: '4.25rem',
	72: '4.5rem',
	76: '4.75rem',
	80: '5rem',
	192: '12rem',
},

Theme Override: Box Shadow

This is a relatively small change. Out of the box, Tailwind utilizes pixel values for box shadows. At wd_s, we simply prefer to use rem values instead of px values for box shadows, so we're replacing the default values.

Usage:

boxShadow: {
	xs: '0 0 0 0.0625rem rgba(0, 0, 0, 0.05)',
	sm: '0 0.0625rem 0.125rem 0 rgba(0, 0, 0, 0.05)',
	default: '0 0.0625rem 0.1875rem 0 rgba(0, 0, 0, 0.1), 0 0.0625rem 0.125rem 0 rgba(0, 0, 0, 0.06)',
	md: '0 0.25rem 0.375rem -0.0625rem rgba(0, 0, 0, 0.1), 0 0.125rem 0.25rem -0.0625rem rgba(0, 0, 0, 0.06)',
	lg: '0 0.625rem 0.9375 -0.1875rem rgba(0, 0, 0, 0.1), 0 0.25rem 0.375rem -0.125rem rgba(0, 0, 0, 0.05)',
	xl: '0 1.25rem 1.5625rem -0.3125rem rgba(0, 0, 0, 0.1), 0 0.625rem 0.625rem -0.3125rem rgba(0, 0, 0, 0.04)',
	'2xl': '0 1.5625rem 3.125rem -10.125rem rgba(0, 0, 0, 0.25)',
	'3xl': '0 2.1875rem 3.75rem -0.9375rem rgba(0, 0, 0, 0.3)',
	inner: 'inset 0 0.125rem 0.25rem 0 rgba(0, 0, 0, 0.06)',
	outline: '0 0 0 0.1875rem rgba(66, 153, 225, 0.5)',
	focus: '0 0 0 0.1875rem rgba(66, 153, 225, 0.5)',
	none: 'none',
},

Theme Override: Screens

Similar to the box shadow overrides, we're simply replacing Tailwind's default breakpoint values with the breakpoints we most frequently use at wd_s.

Usage:

screens: {
	'phone': '300px',
	'tablet-portrait': '600px',
	'wp-admin-bar': '783px',
	'tablet-landscape': '900px',
	'desktop-min': {'min': '1200px'},
	'desktop': '1200px',
	'desktop-large': '1600px',
},

Theme Override: Container

The .container class is a powerful thing in Tailwind and one of the classes that we will use in our markup.

With these few lines, we're telling the .container class to always be centered on the screen using margin: 0 auto and to max out at 1200px on large screens.

Usage:

container: {
	center: true,
	screens: {
		'phone': '100%',
		'desktop': '1200px',
	},
},

Theme Extends

Extending pieces of core Tailwind will add to Tailwind defaults instead of overriding all of the values like we're doing with font sizes, spacing, and other items noted above.

Here, we're just adding another value for backgroundOpacity which, by default, has stops at 0, 25%, 50%, 75%, and 100%.

Usage:

extend: {
	backgroundOpacity: {
		'10': '0.1',
	},
},

Future Proofing

More information on this can be found in the Tailwind documentation.

In Tailwind's own words:

Tailwind v1.8.0 introduced new layers purge mode which will be the default in v2.0. It purges all layers (base, components, and utilities) by default, whereas the previous default mode (conservative, now deprecated) only purged the utilities layer.

With the introduction of the new preserveHtmlElements purge option (which is true by default), we consider this more aggressive purging mode very safe, and it is unlikely to actually be a breaking change in your application.

You should see an even smaller CSS file by default with the new layers mode enabled, and shouldn't see any negative consequences unless you were deliberately relying on the fact that Tailwind was previously not purging the base or components layers by default.

To opt-in to using the new layers mode by default, use the purgeLayersByDefault flag.

Usage:

future: {
	purgeLayersByDefault: true,
},

Plugins: Base

To set some extremely basic core styles for the theme, we're adding a base plugin with some styles for headings and paragraph tags. We don't want to get too opinionated with our code here, so we're being as bare bones as possible so these values will be easy to override in a new project.

Usage:

plugin( function({ addBase, config }) {
	addBase({
		'h1,.h1': {
			fontSize: config( 'theme.fontSize.heading-xl' ),
		},
		'h2,.h2': {
			fontSize: config( 'theme.fontSize.heading-lg' ),
		},
		'h3,.h3': {
			fontSize: config( 'theme.fontSize.heading-md' ),
		},
		'h4,.h4': {
			fontSize: config( 'theme.fontSize.heading-sm' ),
		},
		'h5,.h5': {
			fontSize: config( 'theme.fontSize.heading-xs' ),
		},
		'h6,.h6': {
			fontSize: config( 'theme.fontSize.heading-xs' ),
		},
		'h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6': {
			marginBottom: config( 'theme.spacing.16' ),
		},
		'a': {
			textDecoration: 'underline',
		},
		'p': {
			marginBottom: config( 'theme.spacing.16' ),
			'&:last-child': {
				marginBottom: '0',
			},
		},
	})
}),

Components: Screen Reader Text

These styles allow for hiding a text element from the screen but still allowing it to be read by a screen reader.

Since these styles aren't something that should need to be changed or customized per theme, and they're something we want to include in each project out of the box, it made sense to include them in the config file.

These styles can be used by adding the screen-reader-text class to the markup of an element, or by using @apply screen-reader-text in a Sass partial.

Usage:

plugin( function({ addComponents, config }) {
	const screenReaderText = {
		'.screen-reader-text': {
			clip: 'rect(1px, 1px, 1px, 1px)',
			height: '1px',
			overflow: 'hidden',
			position: 'absolute',
			whiteSpace: 'nowrap',
			width: '1px',
			'&:hover,&:active,&:focus': {
				backgroundColor: config( 'theme.colors.black' ),
				clip: 'auto',
				color: config( 'theme.colors.white' ),
				display: 'block',
				fontSize: config( 'theme.fontSize.base' ),
				fontWeight: config( 'theme.fontWeight.medium' ),
				height: 'auto',
				left: '5px',
				lineHeight: 'normal',
				padding: config( 'theme.spacing.8' ),
				textDecoration: 'none',
				top: '5px',
				width: 'auto',
				zIndex: '100000',
			},
		},
	}

	addComponents( screenReaderText, {
		variants: ['hover', 'active', 'focus'],
	})
}),
Clone this wiki locally