-
Notifications
You must be signed in to change notification settings - Fork 136
Tailwind
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.
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.
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',
},
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'
},
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',
},
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',
},
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',
},
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',
},
},
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',
},
},
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
, andutilities
) by default, whereas the previous default mode (conservative
, now deprecated) only purged theutilities
layer.
With the introduction of the new
preserveHtmlElements
purge option (which istrue
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 thebase
orcomponents
layers by default.
To opt-in to using the new layers mode by default, use the
purgeLayersByDefault
flag.
Usage:
future: {
purgeLayersByDefault: true,
},
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',
},
},
})
}),
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'],
})
}),