diff --git a/src/lib/substituteVariantsAtRules.js b/src/lib/substituteVariantsAtRules.js
--- a/src/lib/substituteVariantsAtRules.js
+++ b/src/lib/substituteVariantsAtRules.js
@@ -124,6 +124,7 @@ const defaultVariantGenerators = (config) => ({
   hover: generatePseudoClassVariant('hover'),
   'focus-within': generatePseudoClassVariant('focus-within'),
   'focus-visible': generatePseudoClassVariant('focus-visible'),
+  'read-only': generatePseudoClassVariant('read-only'),
   focus: generatePseudoClassVariant('focus'),
   active: generatePseudoClassVariant('active'),
   visited: generatePseudoClassVariant('visited'),
diff --git a/stubs/defaultConfig.stub.js b/stubs/defaultConfig.stub.js
--- a/stubs/defaultConfig.stub.js
+++ b/stubs/defaultConfig.stub.js
@@ -807,6 +807,7 @@ module.exports = {
     'visited',
     'checked',
     'empty',
+    'read-only',
     'group-hover',
     'group-focus',
     'focus-within',

diff --git a/tests/variantsAtRule.test.js b/tests/variantsAtRule.test.js
--- a/tests/variantsAtRule.test.js
+++ b/tests/variantsAtRule.test.js
@@ -569,6 +569,27 @@ test('it can generate hover, active and focus variants', () => {
   })
 })
 
+test('it can generate read-only variants', () => {
+  const input = `
+    @variants read-only {
+      .banana { color: yellow; }
+      .chocolate { color: brown; }
+    }
+  `
+
+  const output = `
+    .banana { color: yellow; }
+    .chocolate { color: brown; }
+    .read-only\\:banana:read-only { color: yellow; }
+    .read-only\\:chocolate:read-only { color: brown; }
+  `
+
+  return run(input).then((result) => {
+    expect(result.css).toMatchCss(output)
+    expect(result.warnings().length).toBe(0)
+  })
+})
+
 test('it can generate hover, active and focus variants for multiple classes in one rule', () => {
   const input = `
     @variants hover, focus, active {
@@ -648,7 +669,7 @@ test('variants are generated in the order specified', () => {
 
 test('the built-in variant pseudo-selectors are appended before any pseudo-elements', () => {
   const input = `
-    @variants hover, focus-within, focus-visible, focus, active, group-hover {
+    @variants hover, focus-within, focus-visible, focus, active, group-hover, read-only {
       .placeholder-yellow::placeholder { color: yellow; }
     }
   `
@@ -661,6 +682,7 @@ test('the built-in variant pseudo-selectors are appended before any pseudo-eleme
     .focus\\:placeholder-yellow:focus::placeholder { color: yellow; }
     .active\\:placeholder-yellow:active::placeholder { color: yellow; }
     .group:hover .group-hover\\:placeholder-yellow::placeholder { color: yellow; }
+    .read-only\\:placeholder-yellow:read-only::placeholder { color: yellow; }
   `
 
   return run(input).then((result) => {

Read only variant
There's currently no variant for `<input>` elements with the `readonly` attribute. I added this myself today following the [variant plugin documentation](https://tailwindcss.com/docs/plugins#adding-variants), but wanted to check whether this would be a good inclusion to the framework as a variant users can turn on.

Thoughts? If it's a good fit, I can probably do a PR relatively quickly.

Hey! No concrete plans to add this but since we already have `disabled` I suppose there's no reason to close a PR for it, especially since it would be disabled by default. Feel free to open a PR for it if you like 👍🏻 Closing this though as we only use issues for bug reports.
@weaversam8 Are you still willing to submit a PR to add the `readonly` variant?  I would personally love to see this!
@TheCatLady @skyporter @mendz - I actually no longer have the bandwidth to do this PR right now, sorry 😢 

If anyone else wants to tackle this, it probably isn't too hard. Some reference PRs you can take a look at are #3298, #2385, and #1824.
tests/variantsAtRule.test.js