浏览代码

Tailwind package setup (#8003)

Co-authored-by: Thibaud Colas <thibaudcolas@gmail.com>
Steve Stein 3 年之前
父节点
当前提交
3bf9b65c06

+ 1 - 0
.eslintrc.js

@@ -97,6 +97,7 @@ module.exports = {
         '*.test.tsx',
         '*.test.js',
         'webpack.config.js',
+        'tailwind.config.js',
         '*.stories.js',
         '*.stories.tsx',
         'storybook/**/*',

+ 19 - 0
.stylelintrc.js

@@ -1,6 +1,25 @@
 module.exports = {
   extends: '@wagtail/stylelint-config-wagtail',
   rules: {
+    'scss/at-rule-no-unknown': [
+      true,
+      {
+        ignoreAtRules: [
+          'tailwind',
+          'apply',
+          'variants',
+          'responsive',
+          'screen',
+          'layer',
+        ],
+      },
+    ],
+    'no-invalid-position-at-import-rule': [
+      true,
+      {
+        ignoreAtRules: ['tailwind', 'use'],
+      },
+    ],
     // Would be valuable for strict BEM components but is too hard to enforce with legacy code.
     'no-descending-specificity': null,
   },

+ 12 - 2
client/scss/core.scss

@@ -41,6 +41,15 @@ OTHER PREFIXES
 
 ==============================================================================*/
 
+/* TAILWIND BASE
+These inject Tailwind's base, component and utility styles and any styles registered by plugins of each layer.
+Unused styles created within tailwinds layers won't be compiled into the compiled stylesheet
+https://tailwindcss.com/docs/adding-custom-styles#using-css-and-layer
+*/
+
+@tailwind base;
+@tailwind components;
+
 /* SETTINGS
 These are variables, maps, and fonts.
 * No CSS should be produced by these files
@@ -143,15 +152,16 @@ These are classes that provide overrides.
 
 // UTILITIES: classes that do one simple thing.
 @import 'overrides/utilities.hidden';
-@import 'overrides/utilities.text';
 @import 'overrides/utilities.dropdowns';
 @import 'overrides/utilities.focus';
 @import 'overrides/utilities.visuallyhidden';
 
 // Legacy utilities
-@import 'overrides/utilities.text.legacy';
 @import 'overrides/utilities.legacy';
 
 // PAGES: page-specific overrides
 @import 'overrides/pages.homepage';
 @import 'overrides/pages.page-explorer';
+
+// TAILWIND: This is at the bottom so it can take precedence over other css classes
+@tailwind utilities;

+ 0 - 3
client/scss/overrides/_utilities.text.legacy.scss

@@ -1,3 +0,0 @@
-.unbold {
-  font-weight: normal;
-}

+ 0 - 11
client/scss/overrides/_utilities.text.scss

@@ -1,11 +0,0 @@
-.u-text-transform-uppercase {
-  text-transform: uppercase;
-}
-
-.u-text-weight-normal {
-  font-weight: normal;
-}
-
-.u-para {
-  margin-bottom: 1rem;
-}

+ 2 - 2
client/src/tokens/typography.js

@@ -18,7 +18,7 @@ const systemUIFontStack = [
 
 const monoFontStack = ['monospace', 'serif'];
 
-const fontFamilies = {
+const fontFamily = {
   sans: systemUIFontStack,
   mono: monoFontStack,
 };
@@ -72,7 +72,7 @@ const lineHeight = {
 module.exports = {
   systemUIFontStack,
   monoFontStack,
-  fontFamilies,
+  fontFamily,
   fontSize,
   fontWeight,
   letterSpacing,

+ 1 - 1
client/storybook/main.js

@@ -27,7 +27,7 @@ module.exports = {
             loader: 'postcss-loader',
             options: {
               postcssOptions: {
-                plugins: ['autoprefixer', 'cssnano'],
+                plugins: ['tailwindcss', 'autoprefixer', 'cssnano'],
               },
             },
           },

+ 56 - 0
client/tailwind.config.js

@@ -0,0 +1,56 @@
+const colors = require('./src/tokens/colors');
+const {
+  fontFamily,
+  fontSize,
+  fontWeight,
+  letterSpacing,
+  lineHeight,
+} = require('./src/tokens/typography');
+const { breakpoints } = require('./src/tokens/breakpoints');
+const {
+  borderRadius,
+  borderWidth,
+  boxShadow,
+} = require('./src/tokens/objectStyles');
+const { spacing } = require('./src/tokens/spacing');
+
+const themeColors = Object.fromEntries(
+  Object.entries(colors).map(([key, hues]) => {
+    const shades = Object.fromEntries(
+      Object.entries(hues).map(([k, shade]) => [k, shade.hex]),
+    );
+    return [key, shades];
+  }),
+);
+
+/**
+ * Root Tailwind config, reusable for other projects.
+ */
+module.exports = {
+  prefix: 'w-',
+  theme: {
+    screens: {
+      ...breakpoints,
+    },
+    colors: {
+      ...themeColors,
+      inherit: 'inherit',
+      current: 'currentColor',
+      transparent: 'transparent',
+    },
+    fontFamily,
+    fontSize,
+    fontWeight,
+    lineHeight,
+    letterSpacing,
+    borderRadius,
+    borderWidth,
+    boxShadow: {
+      ...boxShadow,
+      none: 'none',
+    },
+    spacing,
+  },
+  plugins: [],
+  corePlugins: {},
+};

+ 1 - 1
client/webpack.config.js

@@ -407,7 +407,7 @@ module.exports = function exports(env, argv) {
               loader: 'postcss-loader',
               options: {
                 postcssOptions: {
-                  plugins: ['autoprefixer', 'cssnano'],
+                  plugins: ['tailwindcss', 'autoprefixer', 'cssnano'],
                 },
               },
             },

+ 8 - 0
docs/contributing/css_guidelines.rst

@@ -11,6 +11,14 @@ and `Prettier <https://prettier.io/>`_ for formatting.
 You'll need Node.js and npm on your development machine.
 Ensure project dependencies are installed by running ``npm install --no-save``
 
+Tailwind CSS
+-------------
+
+Wagtail uses utility classes via `Tailwind <https://tailwindcss.com/>`_, generated based on values set in the ``tailwind.config.js`` file.
+
+To use these utilities you will need to prefix your class names with ``w-`` to avoid interfering with other predefined styles of similar names.
+
+
 Linting code
 ------------
 

+ 418 - 18
package-lock.json

@@ -40,7 +40,7 @@
         "@typescript-eslint/parser": "^5.8.0",
         "@wagtail/eslint-config-wagtail": "^0.4.0",
         "@wagtail/stylelint-config-wagtail": "^0.3.2",
-        "autoprefixer": "^10.4.0",
+        "autoprefixer": "^10.4.2",
         "babel-loader": "^8.2.3",
         "copy-webpack-plugin": "^10.2.0",
         "css-loader": "^6.5.1",
@@ -52,7 +52,7 @@
         "expose-loader": "^3.1.0",
         "jest": "^26.6.3",
         "mini-css-extract-plugin": "^2.4.5",
-        "postcss": "^8.4.5",
+        "postcss": "^8.4.7",
         "postcss-loader": "^6.2.1",
         "prettier": "^2.5.1",
         "react-axe": "^3.5.4",
@@ -62,6 +62,7 @@
         "sass-loader": "^12.4.0",
         "storybook-django": "^0.3.0",
         "stylelint": "^14.2.0",
+        "tailwindcss": "^3.0.23",
         "ts-jest": "^26.5.6",
         "ts-loader": "^9.2.6",
         "typescript": "^4.5.4",
@@ -10513,6 +10514,29 @@
         "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
       }
     },
+    "node_modules/acorn-node": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
+      "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^7.0.0",
+        "acorn-walk": "^7.0.0",
+        "xtend": "^4.0.2"
+      }
+    },
+    "node_modules/acorn-node/node_modules/acorn": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/acorn-walk": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
@@ -10829,6 +10853,12 @@
         "node": ">=10"
       }
     },
+    "node_modules/arg": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz",
+      "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==",
+      "dev": true
+    },
     "node_modules/argparse": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -14257,6 +14287,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/defined": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+      "dev": true
+    },
     "node_modules/delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -14351,6 +14387,29 @@
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
       "dev": true
     },
+    "node_modules/detective": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
+      "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
+      "dev": true,
+      "dependencies": {
+        "acorn-node": "^1.6.1",
+        "defined": "^1.0.0",
+        "minimist": "^1.1.1"
+      },
+      "bin": {
+        "detective": "bin/detective.js"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/didyoumean": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+      "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+      "dev": true
+    },
     "node_modules/diff-sequences": {
       "version": "26.6.2",
       "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
@@ -14395,6 +14454,12 @@
       "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
       "dev": true
     },
+    "node_modules/dlv": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+      "dev": true
+    },
     "node_modules/doctrine": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -21590,9 +21655,9 @@
       "optional": true
     },
     "node_modules/nanoid": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
-      "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+      "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
       "dev": true,
       "bin": {
         "nanoid": "bin/nanoid.cjs"
@@ -22068,6 +22133,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/object-hash": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
+      "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/object-inspect": {
       "version": "1.12.0",
       "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
@@ -22794,14 +22868,14 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.5",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz",
-      "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==",
+      "version": "8.4.7",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.7.tgz",
+      "integrity": "sha512-L9Ye3r6hkkCeOETQX6iOaWZgjp3LL6Lpqm6EtgbKrgqGGteRMNb9vzBfRL96YOSu8o7x3MfIH9Mo5cPJFGrW6A==",
       "dev": true,
       "dependencies": {
-        "nanoid": "^3.1.30",
+        "nanoid": "^3.3.1",
         "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.1"
+        "source-map-js": "^1.0.2"
       },
       "engines": {
         "node": "^10 || ^12 || >=14"
@@ -22933,6 +23007,50 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/postcss-js": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz",
+      "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==",
+      "dev": true,
+      "dependencies": {
+        "camelcase-css": "^2.0.1"
+      },
+      "engines": {
+        "node": "^12 || ^14 || >= 16"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/postcss/"
+      },
+      "peerDependencies": {
+        "postcss": "^8.3.3"
+      }
+    },
+    "node_modules/postcss-load-config": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.3.tgz",
+      "integrity": "sha512-5EYgaM9auHGtO//ljHH+v/aC/TQ5LHXtL7bQajNAUBKUVKiYE8rYpFms7+V26D9FncaGe2zwCoPQsFKb5zF/Hw==",
+      "dev": true,
+      "dependencies": {
+        "lilconfig": "^2.0.4",
+        "yaml": "^1.10.2"
+      },
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/postcss/"
+      },
+      "peerDependencies": {
+        "ts-node": ">=9.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ts-node": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/postcss-loader": {
       "version": "6.2.1",
       "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
@@ -23133,6 +23251,25 @@
         "postcss": "^8.1.0"
       }
     },
+    "node_modules/postcss-nested": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz",
+      "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==",
+      "dev": true,
+      "dependencies": {
+        "postcss-selector-parser": "^6.0.6"
+      },
+      "engines": {
+        "node": ">=12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/postcss/"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.14"
+      }
+    },
     "node_modules/postcss-normalize-charset": {
       "version": "5.0.2",
       "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.2.tgz",
@@ -26876,6 +27013,110 @@
       "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
       "dev": true
     },
+    "node_modules/tailwindcss": {
+      "version": "3.0.23",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.23.tgz",
+      "integrity": "sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA==",
+      "dev": true,
+      "dependencies": {
+        "arg": "^5.0.1",
+        "chalk": "^4.1.2",
+        "chokidar": "^3.5.3",
+        "color-name": "^1.1.4",
+        "cosmiconfig": "^7.0.1",
+        "detective": "^5.2.0",
+        "didyoumean": "^1.2.2",
+        "dlv": "^1.1.3",
+        "fast-glob": "^3.2.11",
+        "glob-parent": "^6.0.2",
+        "is-glob": "^4.0.3",
+        "normalize-path": "^3.0.0",
+        "object-hash": "^2.2.0",
+        "postcss": "^8.4.6",
+        "postcss-js": "^4.0.0",
+        "postcss-load-config": "^3.1.0",
+        "postcss-nested": "5.0.6",
+        "postcss-selector-parser": "^6.0.9",
+        "postcss-value-parser": "^4.2.0",
+        "quick-lru": "^5.1.1",
+        "resolve": "^1.22.0"
+      },
+      "bin": {
+        "tailwind": "lib/cli.js",
+        "tailwindcss": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=12.13.0"
+      },
+      "peerDependencies": {
+        "autoprefixer": "^10.0.2",
+        "postcss": "^8.0.9"
+      }
+    },
+    "node_modules/tailwindcss/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/tailwindcss/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/tailwindcss/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/tailwindcss/node_modules/quick-lru": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/tailwindcss/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/tapable": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
@@ -37396,6 +37637,25 @@
       "dev": true,
       "requires": {}
     },
+    "acorn-node": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
+      "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
+      "dev": true,
+      "requires": {
+        "acorn": "^7.0.0",
+        "acorn-walk": "^7.0.0",
+        "xtend": "^4.0.2"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.4.1",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+          "dev": true
+        }
+      }
+    },
     "acorn-walk": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
@@ -37643,6 +37903,12 @@
         "readable-stream": "^3.6.0"
       }
     },
+    "arg": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz",
+      "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==",
+      "dev": true
+    },
     "argparse": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -40343,6 +40609,12 @@
         "isobject": "^3.0.1"
       }
     },
+    "defined": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+      "dev": true
+    },
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -40419,6 +40691,23 @@
         }
       }
     },
+    "detective": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
+      "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
+      "dev": true,
+      "requires": {
+        "acorn-node": "^1.6.1",
+        "defined": "^1.0.0",
+        "minimist": "^1.1.1"
+      }
+    },
+    "didyoumean": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+      "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+      "dev": true
+    },
     "diff-sequences": {
       "version": "26.6.2",
       "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
@@ -40459,6 +40748,12 @@
       "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
       "dev": true
     },
+    "dlv": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+      "dev": true
+    },
     "doctrine": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -45947,9 +46242,9 @@
       "optional": true
     },
     "nanoid": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
-      "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+      "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
       "dev": true
     },
     "nanomatch": {
@@ -46352,6 +46647,12 @@
         }
       }
     },
+    "object-hash": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
+      "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
+      "dev": true
+    },
     "object-inspect": {
       "version": "1.12.0",
       "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
@@ -46906,14 +47207,14 @@
       "dev": true
     },
     "postcss": {
-      "version": "8.4.5",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz",
-      "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==",
+      "version": "8.4.7",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.7.tgz",
+      "integrity": "sha512-L9Ye3r6hkkCeOETQX6iOaWZgjp3LL6Lpqm6EtgbKrgqGGteRMNb9vzBfRL96YOSu8o7x3MfIH9Mo5cPJFGrW6A==",
       "dev": true,
       "requires": {
-        "nanoid": "^3.1.30",
+        "nanoid": "^3.3.1",
         "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.1"
+        "source-map-js": "^1.0.2"
       }
     },
     "postcss-colormin": {
@@ -46998,6 +47299,25 @@
         }
       }
     },
+    "postcss-js": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz",
+      "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==",
+      "dev": true,
+      "requires": {
+        "camelcase-css": "^2.0.1"
+      }
+    },
+    "postcss-load-config": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.3.tgz",
+      "integrity": "sha512-5EYgaM9auHGtO//ljHH+v/aC/TQ5LHXtL7bQajNAUBKUVKiYE8rYpFms7+V26D9FncaGe2zwCoPQsFKb5zF/Hw==",
+      "dev": true,
+      "requires": {
+        "lilconfig": "^2.0.4",
+        "yaml": "^1.10.2"
+      }
+    },
     "postcss-loader": {
       "version": "6.2.1",
       "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
@@ -47124,6 +47444,15 @@
         "icss-utils": "^5.0.0"
       }
     },
+    "postcss-nested": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz",
+      "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==",
+      "dev": true,
+      "requires": {
+        "postcss-selector-parser": "^6.0.6"
+      }
+    },
     "postcss-normalize-charset": {
       "version": "5.0.2",
       "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.2.tgz",
@@ -50055,6 +50384,77 @@
         }
       }
     },
+    "tailwindcss": {
+      "version": "3.0.23",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.23.tgz",
+      "integrity": "sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA==",
+      "dev": true,
+      "requires": {
+        "arg": "^5.0.1",
+        "chalk": "^4.1.2",
+        "chokidar": "^3.5.3",
+        "color-name": "^1.1.4",
+        "cosmiconfig": "^7.0.1",
+        "detective": "^5.2.0",
+        "didyoumean": "^1.2.2",
+        "dlv": "^1.1.3",
+        "fast-glob": "^3.2.11",
+        "glob-parent": "^6.0.2",
+        "is-glob": "^4.0.3",
+        "normalize-path": "^3.0.0",
+        "object-hash": "^2.2.0",
+        "postcss": "^8.4.6",
+        "postcss-js": "^4.0.0",
+        "postcss-load-config": "^3.1.0",
+        "postcss-nested": "5.0.6",
+        "postcss-selector-parser": "^6.0.9",
+        "postcss-value-parser": "^4.2.0",
+        "quick-lru": "^5.1.1",
+        "resolve": "^1.22.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "quick-lru": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+          "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
     "tapable": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",

+ 3 - 2
package.json

@@ -63,7 +63,7 @@
     "@typescript-eslint/parser": "^5.8.0",
     "@wagtail/eslint-config-wagtail": "^0.4.0",
     "@wagtail/stylelint-config-wagtail": "^0.3.2",
-    "autoprefixer": "^10.4.0",
+    "autoprefixer": "^10.4.2",
     "babel-loader": "^8.2.3",
     "copy-webpack-plugin": "^10.2.0",
     "css-loader": "^6.5.1",
@@ -75,7 +75,7 @@
     "expose-loader": "^3.1.0",
     "jest": "^26.6.3",
     "mini-css-extract-plugin": "^2.4.5",
-    "postcss": "^8.4.5",
+    "postcss": "^8.4.7",
     "postcss-loader": "^6.2.1",
     "prettier": "^2.5.1",
     "react-axe": "^3.5.4",
@@ -85,6 +85,7 @@
     "sass-loader": "^12.4.0",
     "storybook-django": "^0.3.0",
     "stylelint": "^14.2.0",
+    "tailwindcss": "^3.0.23",
     "ts-jest": "^26.5.6",
     "ts-loader": "^9.2.6",
     "typescript": "^4.5.4",

+ 18 - 0
tailwind.config.js

@@ -0,0 +1,18 @@
+const baseConfig = require('./client/tailwind.config');
+
+/**
+ * Tailwind config file for Wagtail itself.
+ */
+module.exports = {
+  presets: [baseConfig],
+  content: [
+    './wagtail/**/*.{py,html,ts,tsx}',
+    './wagtail/**/static_src/**/*.js',
+    './client/**/*.{js,ts,tsx}',
+    './docs/**/*.{md,rst}',
+  ],
+  corePlugins: {
+    // Risk of clashing with existing styles.
+    preflight: false,
+  },
+};

+ 1 - 1
wagtail/contrib/styleguide/templates/wagtailstyleguide/base.html

@@ -585,7 +585,7 @@
                     Inline dropdown components, primarily used in the listing.
                 </p>
 
-                <div class="c-dropdown u-para t-default" data-dropdown="">
+                <div class="c-dropdown t-default" data-dropdown="">
                     <a class="c-dropdown__button  u-btn-current">
                         More
                         <div data-dropdown-toggle="" class="o-icon  c-dropdown__toggle c-dropdown__togle--icon  [ icon icon-arrow-down ]">

+ 1 - 1
wagtail/images/templates/wagtailimages/images/edit.html

@@ -81,7 +81,7 @@
 
                 <div class="row">
                     <div class="col8 divider-after">
-                        <h2 class="label no-float u-text-transform-uppercase">{% trans "Focal point" %} <span class="u-text-weight-normal">{% trans "(optional)" %}</span></h2>
+                        <h2 class="label no-float w-uppercase">{% trans "Focal point" %} <span class="w-font-normal">{% trans "(optional)" %}</span></h2>
                         <p>{% trans "To define this image's most important region, drag a box over the image above." %} {% if image.has_focal_point %}({% trans "Current focal point shown" %}){% endif %}</p>
 
                         <button class="button button-secondary no remove-focal-point" type="button">{% trans "Remove focal area" %}</button>