diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
 # Revision history for djot
 
+## 0.1.1.1 -- 2024-03-03
+
+* Revert "Djot.Blocks: use ByteString directly in `toIdentifier` (#1)"
+  This caused problems for UTF-8 sequences that contained the
+  byte 0xa0, which B8.words treats as a space character.
+
+* AST: avoid using B8.words in normalizeLabel.
+
+* Avoid using isSpace in attribute parsing. isSpace matches a byte 0x0a,
+  which can break up a UTF-8 sequence. Limit to ASCII whitespace.
+
+  * Add test with UTF-8 identifier. See jgm/pandoc#9541.
+
 ## 0.1.1.0 -- 2024-02-29
 
 * Add Data instances to everything in the AST [API change].
diff --git a/djot.cabal b/djot.cabal
--- a/djot.cabal
+++ b/djot.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               djot
-version:            0.1.1.0
+version:            0.1.1.1
 synopsis:           Parser and renderer for djot light markup syntax.
 description:        Djot (<https://djot.net>) is a light markup language.
                     This package provides a data structure to represent
diff --git a/src/Djot/AST.hs b/src/Djot/AST.hs
--- a/src/Djot/AST.hs
+++ b/src/Djot/AST.hs
@@ -306,7 +306,9 @@
   deriving (Show, Ord, Eq, Semigroup, Monoid, Typeable, Data, Generic)
 
 normalizeLabel :: ByteString -> ByteString
-normalizeLabel = B8.unwords . B8.words
+normalizeLabel = B8.unwords . B8.splitWith isWs
+ where
+  isWs c = c == ' ' || c == '\t' || c == '\r' || c == '\n'
 
 insertReference :: ByteString -> (ByteString, Attr) -> ReferenceMap
                 -> ReferenceMap
diff --git a/src/Djot/Attributes.hs b/src/Djot/Attributes.hs
--- a/src/Djot/Attributes.hs
+++ b/src/Djot/Attributes.hs
@@ -8,7 +8,7 @@
   , AttrParseResult(..)
   )
 where
-import Data.Char (isAlphaNum, isSpace, isPunctuation)
+import Data.Char (isAlphaNum, isPunctuation)
 import Djot.AST (Attr(..))
 import Djot.Parse
 import Data.ByteString (ByteString)
@@ -177,7 +177,7 @@
    getAttrVal _ = mempty
 
 isNameChar :: Char -> Bool
-isNameChar c = not (isSpace c)
+isNameChar c = not (isWs c)
     && (not (isPunctuation c) || c == ':' || c == '_' || c == '-')
 
 isKeyChar :: Char -> Bool
diff --git a/src/Djot/Blocks.hs b/src/Djot/Blocks.hs
--- a/src/Djot/Blocks.hs
+++ b/src/Djot/Blocks.hs
@@ -26,6 +26,7 @@
 import Data.ByteString (ByteString)
 import Control.Monad (replicateM_, void, mzero, unless, when, guard, foldM)
 import Data.List.NonEmpty (NonEmpty(..))
+import Data.List (intercalate)
 import qualified Data.List.NonEmpty as NonEmpty
 import Data.Set (Set)
 import qualified Data.Set as Set
@@ -1126,11 +1127,12 @@
       closeContainingSections lev
     _ -> pure ()
 
+-- TODO avoid detour through String
 toIdentifier :: ByteString -> ByteString
 toIdentifier bs =
   if null parts
      then "sec"
-     else B8.intercalate (B8.singleton '-') parts
+     else strToUtf8 $ intercalate "-" parts
  where
    isSym = (`elem` ("][~!@#$%^&*(){}`,.<>\\|=+/" :: [Char]))
-   parts = B8.words $ B8.map (\c -> if isSym c then ' ' else c) bs
+   parts = words $ map (\c -> if isSym c then ' ' else c) $ utf8ToStr bs
diff --git a/test/regression.test b/test/regression.test
--- a/test/regression.test
+++ b/test/regression.test
@@ -91,3 +91,12 @@
 </blockquote>
 ```
 
+```
+{#convertibilità}
+# Convertibilità
+.
+<section id="convertibilità">
+<h1>Convertibilità</h1>
+</section>
+```
+
