diff --git a/ascii-th.cabal b/ascii-th.cabal
--- a/ascii-th.cabal
+++ b/ascii-th.cabal
@@ -1,7 +1,7 @@
 cabal-version: 3.0
 
 name: ascii-th
-version: 1.1.1.0
+version: 1.2.0.0
 synopsis: Template Haskell support for ASCII
 category: Data, Text
 
@@ -31,13 +31,15 @@
         ascii-case ^>= 1.0
       , ascii-caseless ^>= 0.0
       , ascii-char ^>= 1.0
-      , ascii-superset ^>= 1.1.0
+      , ascii-superset ^>= 1.2.4
       , base ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17
 
 library
     import: base
     hs-source-dirs: library
-    default-extensions: NoImplicitPrelude TemplateHaskell ViewPatterns
+    default-extensions: AllowAmbiguousTypes DataKinds FlexibleContexts
+        KindSignatures NoImplicitPrelude ScopedTypeVariables
+        TemplateHaskell TypeApplications ViewPatterns
     build-depends:
         template-haskell ^>= 2.16 || ^>= 2.17 || ^>= 2.18 || ^>= 2.19
     exposed-modules:
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,17 @@
+### 1.2.0.0 (2023-01-06)
+
+The constraints on `lower` and `upper` quasi-quotations in an expression context
+are changes from `FromString` to `ToCasefulString 'LowerCase` and
+`ToCasefulString 'UpperCase` respectively. This expands the range of types
+inhabited by lower/uppercase quotes to include `ASCII'lower` and `ASCII'upper`,
+which were previously not able to be expressed using quasi-quotations.
+
+Additions to `ASCII.TemplateHaskell`:
+
+```haskell
+upperStringExp, lowerStringExp :: [CaselessChar] -> Q Exp
+```
+
 ### 1.1.1.0 (2023-01-03)
 
 New in `ASCII.QuasiQuoters`:
diff --git a/library/ASCII/QuasiQuoters.hs b/library/ASCII/QuasiQuoters.hs
--- a/library/ASCII/QuasiQuoters.hs
+++ b/library/ASCII/QuasiQuoters.hs
@@ -19,16 +19,13 @@
 import ASCII.Char (Char)
 import Control.Monad (return, (>=>))
 import Control.Monad.Fail (MonadFail, fail)
-import Data.Functor ((<$>))
 import Data.Maybe (Maybe (..))
 import Language.Haskell.TH.Quote (QuasiQuoter (..))
 import Language.Haskell.TH.Syntax (Exp, Pat, Q)
 
-import qualified ASCII.Case as Case
 import qualified ASCII.Superset as S
 import qualified ASCII.TemplateHaskell as TH
 import qualified Data.Char as Unicode
-import qualified Data.List as List
 import qualified Data.String as Unicode
 
 {-| An expression pattern corresponding to an ASCII character
@@ -148,11 +145,14 @@
 
 === In an expression context
 
-The expression can become any type belonging to the 'ASCII.Superset.FromString'
-class. Any letters in the quoted content will be converted to lower case.
+The expression can become any type satisfying the
+@('ASCII.Superset.ToCasefulString' ''LowerCase')@ constraint.
+Any letters in the quoted content will be converted to lower case.
 
 @
-[lower|Hello!|] == ("hello!" :: 'Text')
+[lower|Hello!|] == ("hello!" :: 'Data.Text.Text')
+
+[lower|Hello!|] == ("hello!" :: 'ASCII'lower' 'Data.ByteString.ByteString')
 @
 
 === In a pattern context
@@ -176,7 +176,8 @@
 
 -}
 lower :: QuasiQuoter
-lower = ofCase LowerCase
+lower = expPatQQ requireAsciiListCI TH.lowerStringExp
+    (\x -> TH.isStringPat (S.toCasefulString @'LowerCase x))
 
 {-| An expression or pattern corresponding to an ASCII string where all the
 letters are of upper case
@@ -186,11 +187,14 @@
 
 === In an expression context
 
-The expression can become any type belonging to the 'ASCII.Superset.FromString'
-class. Any letters in the quoted content will be converted to upper case.
+The expression can become any type satisfying the
+@('ASCII.Superset.ToCasefulString' ''UpperCase')@ constraint.
+Any letters in the quoted content will be converted to upper case.
 
 @
 [upper|Hello!|] == ("HELLO!" :: 'Text')
+
+[upper|Hello!|] == ("HELLO!" :: 'ASCII'upper' 'Data.ByteString.ByteString')
 @
 
 === In a pattern context
@@ -214,10 +218,8 @@
 
 -}
 upper :: QuasiQuoter
-upper = ofCase UpperCase
-
-ofCase :: Case -> QuasiQuoter
-ofCase c = expPatQQ (requireAsciiToCase c) TH.isStringExp TH.isStringPat
+upper = expPatQQ requireAsciiListCI TH.upperStringExp
+    (\x -> TH.isStringPat (S.toCasefulString @'UpperCase x))
 
 {-| Require the string to consist of exactly one ASCII character -}
 requireOneAscii :: Unicode.String -> Q Char
@@ -243,11 +245,6 @@
 and return them with letter case discarded -}
 requireAsciiListCI :: Unicode.String -> Q [CaselessChar]
 requireAsciiListCI = S.toCaselessCharListMaybe || "Must be only ASCII characters."
-
-{-| Require the string to consist of all ASCII characters,
-and return them converted to the given case -}
-requireAsciiToCase :: Case -> Unicode.String -> Q [Char]
-requireAsciiToCase c s = List.map (Case.toCase c) <$> requireAsciiList s
 
 (||) :: (a -> Maybe b) -> Unicode.String -> a -> Q b
 f || msg = \a -> case f a of Just b -> return b; Nothing -> fail msg
diff --git a/library/ASCII/TemplateHaskell.hs b/library/ASCII/TemplateHaskell.hs
--- a/library/ASCII/TemplateHaskell.hs
+++ b/library/ASCII/TemplateHaskell.hs
@@ -9,12 +9,14 @@
     {- ** Character -} isCharExp, isCharPat,
     {- ** String -} isStringExp, isStringPat,
     {- ** Caseless string -} caselessIsStringPat,
+    {- ** Single-case string -} upperStringExp, lowerStringExp,
   )
   where
 
 import qualified ASCII.Char as ASCII
 import qualified ASCII.Superset as S
 
+import ASCII.Case (Case (..))
 import ASCII.Caseless (CaselessChar)
 import Data.Data (Data)
 import Data.Maybe (Maybe (..))
@@ -132,3 +134,17 @@
 class, as if it were a list of 'CaselessChar' -}
 caselessIsStringPat :: [CaselessChar] -> Q Pat
 caselessIsStringPat xs = [p| (S.toCaselessCharListMaybe -> Just $(caselessListPat xs)) |]
+
+{-| Expression with a @'ASCII.Superset.ToCasefulString 'UpperCase'@ constraint -}
+upperStringExp :: [CaselessChar] -> Q Exp
+upperStringExp xs = [| toUpper $(caselessListExp xs) |]
+
+{-| Expression with a @'ASCII.Superset.ToCasefulString 'LowerCase'@ constraint -}
+lowerStringExp :: [CaselessChar] -> Q Exp
+lowerStringExp xs = [| toLower $(caselessListExp xs) |]
+
+toUpper :: S.ToCasefulString 'UpperCase string => [CaselessChar] -> string
+toUpper = S.toCasefulString @'UpperCase
+
+toLower :: S.ToCasefulString 'LowerCase string => [CaselessChar] -> string
+toLower = S.toCasefulString @'LowerCase
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -3,9 +3,11 @@
 import qualified ASCII.QuasiQuoters as QQ
 import qualified ASCII.TemplateHaskell as TH
 
+import ASCII.CaseRefinement (ASCII'lower, ASCII'upper, asciiCaseUnsafe)
 import ASCII.Char (Char (..))
 import ASCII.Refinement (ASCII, asciiUnsafe)
 
+import Data.ByteString (ByteString)
 import Data.Function (($))
 import Data.String (String)
 import Data.Text (Text)
@@ -13,7 +15,7 @@
 import Prelude (Integer)
 import System.IO (IO)
 
-import qualified ASCII.Caseless as Caseless
+import qualified ASCII.Caseless as CI
 import qualified Data.ByteString.Builder as BS.Builder
 import qualified Data.ByteString.Lazy as LBS
 
@@ -46,10 +48,10 @@
 
         describe "caseless" $ do
             it "can be [CaselessChar]" $ shouldBe [QQ.caseless|Hello!|]
-                [Caseless.LetterH, Caseless.LetterE, Caseless.LetterL,
-                Caseless.LetterL, Caseless.LetterO, Caseless.ExclamationMark]
+                [CI.LetterH, CI.LetterE, CI.LetterL,
+                CI.LetterL, CI.LetterO, CI.ExclamationMark]
             it "can be a pattern over [CaselessChar]" $ do
-                let x = case [Caseless.LetterH, Caseless.LetterI] of
+                let x = case [CI.LetterH, CI.LetterI] of
                           [QQ.caseless|Bye|] -> 1; [QQ.caseless|Hi|] -> 2; _ -> 3
                 shouldBe @Integer x 2
             it "can be a pattern over Text" $ do
@@ -63,30 +65,26 @@
 
         describe "upper" $ do
             it "can be Text" $ shouldBe [QQ.upper|Hello!|] ("HELLO!" :: Text)
+            it "can be case-refined" $ shouldBe [QQ.upper|Hello!|]
+                (asciiCaseUnsafe "HELLO!" :: ASCII'upper ByteString)
             it "can match Text" $ do
                 let x = case "HI!" :: Text of
-                          [QQ.upper|wow|] -> 1
-                          [QQ.upper|Hi!|] -> 2
-                          _ -> 3
+                          [QQ.upper|wow|] -> 1; [QQ.upper|Hi!|] -> 2; _ -> 3
                 shouldBe @Integer x 2
             it "only matches all upper-case Text" $ do
-                let x = case "Hi!" :: Text of
-                          [QQ.upper|Hi!|] -> 1
-                          _ -> 2
+                let x = case "Hi!" :: Text of [QQ.upper|Hi!|] -> 1; _ -> 2
                 shouldBe @Integer x 2
 
         describe "lower" $ do
             it "can be Text" $ shouldBe [QQ.lower|Hello!|] ("hello!" :: Text)
+            it "can be case-refined" $ shouldBe [QQ.lower|Hello!|]
+                (asciiCaseUnsafe "hello!" :: ASCII'lower ByteString)
             it "can match Text" $ do
                 let x = case "hi!" :: Text of
-                          [QQ.lower|wow|] -> 1
-                          [QQ.lower|Hi!|] -> 2
-                          _ -> 3
+                          [QQ.lower|wow|] -> 1; [QQ.lower|Hi!|] -> 2; _ -> 3
                 shouldBe @Integer x 2
             it "only matches all lower-case Text" $ do
-                let x = case "Hi!" :: Text of
-                          [QQ.lower|Hi!|] -> 1
-                          _ -> 2
+                let x = case "Hi!" :: Text of [QQ.lower|Hi!|] -> 1; _ -> 2
                 shouldBe @Integer x 2
 
     describe "ASCII.TemplateHaskell" $ do
@@ -97,9 +95,10 @@
 
         describe "charPat" $ do
             it "is a Char pattern" $ do
-                let x = case SmallLetterS of $(TH.charPat SmallLetterR) -> 1;
-                                             $(TH.charPat SmallLetterS) -> 2;
-                                             _ -> 3
+                let x = case SmallLetterS of
+                          $(TH.charPat SmallLetterR) -> 1;
+                          $(TH.charPat SmallLetterS) -> 2;
+                          _ -> 3
                 shouldBe @Integer x 2
 
         describe "charListExp" $ do
@@ -126,15 +125,16 @@
 
         describe "isCharPat" $ do
             it "can be a Word8 pattern" $ do
-                let x = case (66 :: Word8) of $(TH.isCharPat CapitalLetterA) -> 1
-                                              $(TH.isCharPat CapitalLetterB) -> 2
-                                              _ -> 3
+                let x = case (66 :: Word8) of
+                          $(TH.isCharPat CapitalLetterA) -> 1
+                          $(TH.isCharPat CapitalLetterB) -> 2
+                          _ -> 3
                 shouldBe @Integer x 2
 
         describe "caselessIsStringPat" $ do
             it "matches a cased string regardless of its case" $ do
                 let x = case [QQ.string|Hi!|] :: Text of
-                      $(TH.caselessIsStringPat [Caseless.LetterG, Caseless.LetterO, Caseless.FullStop]) -> 1
-                      $(TH.caselessIsStringPat [Caseless.LetterH, Caseless.LetterI, Caseless.ExclamationMark]) -> 2
+                      $(TH.caselessIsStringPat [CI.LetterG, CI.LetterO, CI.FullStop]) -> 1
+                      $(TH.caselessIsStringPat [CI.LetterH, CI.LetterI, CI.ExclamationMark]) -> 2
                       _ -> 3
                 shouldBe @Integer x 2
