diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,11 +9,33 @@
 User-visible changes in hledger-web.
 See also the hledger changelog.
 
+# 1.27 2022-09-01
+
+Improvements
+
+- Improve the add form's layout and space usage.
+
+- Pre-fill the add form's date field.
+
+- Highlight today in the add form's date picker.
+
+- Focus the add form's description field by default.
+
+- Allow an empty description in the add form.
+
+- Use hledger 1.27
+
+Fixes
+
+- Respect the add form's file selector again.
+  (Simon Michael, Kerstin, #1229)
+
 # 1.26.1 2022-07-11
 
 - Uses hledger 1.26.1.
 
 # 1.26 2022-06-04
+
 Fixes
 
 - Don't add link URLs when printing.
diff --git a/Hledger/Web/Foundation.hs b/Hledger/Web/Foundation.hs
--- a/Hledger/Web/Foundation.hs
+++ b/Hledger/Web/Foundation.hs
@@ -84,11 +84,12 @@
 -- usually require access to the AppRoute datatype. Therefore, we
 -- split these actions into two functions and place them in separate files.
 mkYesodData "App" $(parseRoutesFile "config/routes")
+-- ^ defines things like:
+-- type Handler = HandlerFor App   -- HandlerT App IO, https://www.yesodweb.com/book/routing-and-handlers#routing-and-handlers_handler_monad
+-- type Widget = WidgetFor App ()  -- WidgetT App IO (), https://www.yesodweb.com/book/widgets
 
--- | A convenience alias.
 type AppRoute = Route App
-
-type Form x = Html -> MForm (HandlerFor App) (FormResult x, Widget)
+type Form a = Html -> MForm Handler (FormResult a, Widget)
 
 -- Please see the documentation for the Yesod typeclass. There are a number
 -- of settings which can be configured by overriding methods here.
diff --git a/Hledger/Web/Handler/AddR.hs b/Hledger/Web/Handler/AddR.hs
--- a/Hledger/Web/Handler/AddR.hs
+++ b/Hledger/Web/Handler/AddR.hs
@@ -35,12 +35,11 @@
 
   ((res, view), enctype) <- runFormPost $ addForm j today
   case res of
-    FormSuccess res' -> do
-      let t = txnTieKnot res'
-      -- XXX(?) move into balanceTransaction
-      liftIO $ ensureJournalFileExists (journalFilePath j)
-      -- XXX why not journalAddTransaction ?
-      liftIO $ appendToJournalFileOrStdout (journalFilePath j) (showTransaction t)
+    FormSuccess (t,f) -> do
+      let t' = txnTieKnot t
+      liftIO $ do
+        ensureJournalFileExists f
+        appendToJournalFileOrStdout f (showTransaction t')
       setMessage "Transaction added."
       redirect JournalR
     FormMissing -> showForm view enctype
@@ -52,7 +51,7 @@
       sendResponse =<< defaultLayout [whamlet|
         <h2>Add transaction
         <div .row style="margin-top:1em">
-          <form#addform.form.col-xs-12.col-md-8 method=post enctype=#{enctype}>
+          <form#addform.form.col-xs-12.col-sm-11 method=post enctype=#{enctype}>
             ^{view}
       |]
 
diff --git a/Hledger/Web/Handler/EditR.hs b/Hledger/Web/Handler/EditR.hs
--- a/Hledger/Web/Handler/EditR.hs
+++ b/Hledger/Web/Handler/EditR.hs
@@ -15,7 +15,7 @@
 import Hledger.Web.Widget.Common
        (fromFormSuccess, helplink, journalFile404, writeJournalTextIfValidAndChanged)
 
-editForm :: FilePath -> Text -> Markup -> MForm Handler (FormResult Text, Widget)
+editForm :: FilePath -> Text -> Form Text
 editForm f txt =
   identifyForm "edit" $ \extra -> do
     (tRes, tView) <- mreq textareaField fs (Just (Textarea txt))
diff --git a/Hledger/Web/Widget/AddForm.hs b/Hledger/Web/Widget/AddForm.hs
--- a/Hledger/Web/Widget/AddForm.hs
+++ b/Hledger/Web/Widget/AddForm.hs
@@ -14,7 +14,7 @@
 import Data.Bifunctor (first)
 import Data.Foldable (toList)
 import Data.List (dropWhileEnd, unfoldr)
-import Data.Maybe (isJust)
+import Data.Maybe (isJust, fromMaybe)
 import qualified Data.Set as S
 import Data.Text (Text)
 import Data.Text.Encoding.Base64 (encodeBase64)
@@ -25,17 +25,13 @@
 import Yesod
 
 import Hledger
+import Hledger.Web.Foundation (App, Handler, Widget)
 import Hledger.Web.Settings (widgetFile)
+import Data.Function ((&))
 
-addModal ::
-     ( MonadWidget m
-     , r ~ Route (HandlerSite m)
-     , m ~ WidgetFor (HandlerSite m)
-     , RenderMessage (HandlerSite m) FormMessage
-     )
-  => r -> Journal -> Day -> m ()
+addModal :: Route App -> Journal -> Day -> Widget
 addModal addR j today = do
-  (addView, addEnctype) <- generateFormPost (addForm j today)
+  (addView, addEnctype) <- handlerToWidget $ generateFormPost (addForm j today)
   [whamlet|
 <div .modal #addmodal tabindex="-1" role="dialog" aria-labelledby="addLabel" aria-hidden="true">
   <div .modal-dialog .modal-lg>
@@ -48,106 +44,88 @@
           ^{addView}
 |]
 
-addForm ::
-     (site ~ HandlerSite m, RenderMessage site FormMessage, MonadHandler m)
-  => Journal
-  -> Day
-  -> Markup
-  -> MForm m (FormResult Transaction, WidgetFor site ())
+addForm :: Journal -> Day -> Markup -> MForm Handler (FormResult (Transaction,FilePath), Widget)
 addForm j today = identifyForm "add" $ \extra -> do
-  (dateRes, dateView) <- mreq dateField dateFS Nothing
-  (descRes, descView) <- mreq textField descFS Nothing
-  (acctRes, _) <- mreq listField acctFS Nothing
-  (amtRes, _) <- mreq listField amtFS Nothing
-  let (postRes, displayRows) = validatePostings acctRes amtRes
-
-  -- bindings used in add-form.hamlet
-  let descriptions = foldMap S.fromList [journalPayeesDeclaredOrUsed j, journalDescriptions j]
-      journals = fst <$> jfiles j
-
-  pure (validateTransaction dateRes descRes postRes, $(widgetFile "add-form"))
-
+  let  -- bindings used in add-form.hamlet
+    descriptions = foldMap S.fromList [journalPayeesDeclaredOrUsed j, journalDescriptions j]
+    files = fst <$> jfiles j
+  (dateRes, dateView) <- mreq dateField dateSettings Nothing
+  (descRes, descView) <- mopt textField descSettings Nothing
+  (acctsRes, _)       <- mreq listField acctSettings Nothing
+  (amtsRes, _)        <- mreq listField amtSettings  Nothing
+  (fileRes, fileView) <- mreq fileField' fileSettings Nothing
+  let
+    (postingsRes, displayRows) = validatePostings acctsRes amtsRes
+    formRes = validateTransaction dateRes descRes postingsRes fileRes
+  return (formRes, $(widgetFile "add-form"))
   where
-    dateFS = FieldSettings "date" Nothing Nothing (Just "date")
-      [("class", "form-control input-lg"), ("placeholder", "Date")]
-    descFS = FieldSettings "desc" Nothing Nothing (Just "description")
-      [("class", "form-control input-lg typeahead"), ("placeholder", "Description"), ("size", "40")]
-    acctFS = FieldSettings "amount" Nothing Nothing (Just "account") []
-    amtFS = FieldSettings "amount" Nothing Nothing (Just "amount") []
-    dateField = checkMMap (pure . validateDate) (T.pack . show) textField
-    validateDate s =
-      first (const ("Invalid date format" :: Text)) $
-      fixSmartDateStrEither' today (T.strip s)
-
+    -- custom fields
+    dateField = textField & checkMMap (pure . validateDate) (T.pack . show)
+      where
+        validateDate s =
+          first (const ("Invalid date format" :: Text)) $
+          fixSmartDateStrEither' today (T.strip s)
     listField = Field
       { fieldParse = const . pure . Right . Just . dropWhileEnd T.null
-      , fieldView = error "Don't render using this!"  -- PARTIAL:
+      , fieldView = error' "listField should not be used for rendering"  -- PARTIAL:
       , fieldEnctype = UrlEncoded
       }
-
-    -- Used in add-form.hamlet
-    toBloodhoundJson :: [Text] -> Markup
-    toBloodhoundJson ts =
-      -- This used to work, but since 1.16, it seems like something changed.
-      -- toJSON ("a"::Text) gives String "a" instead of "a", etc.
-      -- preEscapedString . escapeJSSpecialChars . show . toJSON
-      preEscapedText $ T.concat [
-        "[",
-        T.intercalate "," $ map (
-          ("{\"value\":" <>).
-          (<> "}").
-          -- This will convert a value such as ``hledger!`` into
-          -- ``atob("aGxlZGdlciE=")``. When this gets evaluated on the client,
-          -- the resulting string is ``hledger!`` again. The same data is
-          -- passed, but the user-controlled bit of that string can only use
-          -- characters [a-zA-Z0-9+=/], making it impossible to break out of
-          -- string context.
-          b64wrap
-          ) ts,
-        "]"
-        ]
-b64wrap :: Text -> Text
-b64wrap = ("atob(\""<>) . (<>"\")") . encodeBase64
+    fileField' :: Field Handler FilePath
+    fileField' = selectFieldList [(T.pack f, f) | f <- fs] & check validateFilepath
+      where
+        fs = journalFilePaths j
+        validateFilepath :: FilePath -> Either FormMessage FilePath
+        validateFilepath f
+          | f `elem` fs = Right f
+          | otherwise = Left $ MsgInputNotFound $ T.pack f
+    -- field settings
+    dateSettings = FieldSettings "date" Nothing Nothing (Just "date") [("class", "form-control input-lg"), ("placeholder", "Date")]
+    descSettings = FieldSettings "desc" Nothing Nothing (Just "description") [("class", "form-control input-lg typeahead"), ("placeholder", "Description"), ("size", "40")]
+    acctSettings = FieldSettings "account" Nothing Nothing (Just "account") []
+    amtSettings  = FieldSettings "amount" Nothing Nothing (Just "amount") []
+    fileSettings = FieldSettings "file" Nothing Nothing (Just "file") [("class", "form-control input-lg")]
 
 validateTransaction ::
-     FormResult Day
-  -> FormResult Text
-  -> FormResult [Posting]
-  -> FormResult Transaction
-validateTransaction dateRes descRes postingsRes =
-  case makeTransaction <$> dateRes <*> descRes <*> postingsRes of
-    FormSuccess txn -> case balanceTransaction defbalancingopts txn of
-      Left e -> FormFailure [T.pack e]
-      Right txn' -> FormSuccess txn'
+     FormResult Day -> FormResult (Maybe Text) -> FormResult [Posting] -> FormResult FilePath
+  -> FormResult (Transaction, FilePath)
+validateTransaction dateRes descRes postingsRes fileRes =
+  case makeTransaction <$> dateRes <*> descRes <*> postingsRes <*> fileRes of
+    FormSuccess (txn,f) -> case balanceTransaction defbalancingopts txn of
+      Left e     -> FormFailure [T.pack e]
+      Right txn' -> FormSuccess (txn',f)
     x -> x
   where
-    makeTransaction date desc postings =
-      nulltransaction {tdate = date, tdescription = desc, tpostings = postings}
-
+    makeTransaction date mdesc postings f =
+      (nulltransaction {
+         tdate = date
+        ,tdescription = fromMaybe "" mdesc
+        ,tpostings = postings
+        ,tsourcepos = (initialPos f, initialPos f)
+        }, f)
 
 -- | Parse a list of postings out of a list of accounts and a corresponding list
 -- of amounts
 validatePostings ::
-     FormResult [Text]
-  -> FormResult [Text]
+     FormResult [Text] -> FormResult [Text]
   -> (FormResult [Posting], [(Int, (Text, Text, Maybe Text, Maybe Text))])
-validatePostings acctRes amtRes = let
+validatePostings acctsRes amtsRes = let
 
   -- Zip accounts and amounts, fill in missing values and drop empty rows.
   rows :: [(Text, Text)]
-  rows = filter (/= ("", "")) $ zipDefault "" (formSuccess [] acctRes) (formSuccess [] amtRes)
+  rows = filter (/= ("", "")) $ zipDefault "" (formSuccess [] acctsRes) (formSuccess [] amtsRes)
 
   -- Parse values and check for incomplete rows with only an account or an amount.
   -- The boolean in unfoldr state is for special handling of 'missingamt', where
   -- one row may have only an account and not an amount.
   postings :: [(Text, Text, Either (Maybe Text, Maybe Text) Posting)]
   postings = unfoldr go (True, rows)
-  go (True, (x, ""):y:xs) = Just ((x, "", zipRow (checkAccount x) (Left "Missing amount")), (True, y:xs))
-  go (True, (x, ""):xs) = Just ((x, "", zipRow (checkAccount x) (Right missingamt)), (False, xs))
-  go (False, (x, ""):xs) = Just ((x, "", zipRow (checkAccount x) (Left "Missing amount")), (False, xs))
-  go (_, ("", y):xs) = Just (("", y, zipRow (Left "Missing account") (checkAmount y)), (False, xs))
-  go (_, (x, y):xs) = Just ((x, y, zipRow (checkAccount x) (checkAmount y)), (True, xs))
-  go (_, []) = Nothing
+    where
+      go (True, (x, ""):y:xs) = Just ((x, "", zipRow (checkAccount x) (Left "Missing amount")), (True, y:xs))
+      go (True, (x, ""):xs) = Just ((x, "", zipRow (checkAccount x) (Right missingamt)), (False, xs))
+      go (False, (x, ""):xs) = Just ((x, "", zipRow (checkAccount x) (Left "Missing amount")), (False, xs))
+      go (_, ("", y):xs) = Just (("", y, zipRow (Left "Missing account") (checkAmount y)), (False, xs))
+      go (_, (x, y):xs) = Just ((x, y, zipRow (checkAccount x) (checkAmount y)), (True, xs))
+      go (_, []) = Nothing
 
   zipRow (Left e) (Left e') = Left (Just e, Just e')
   zipRow (Left e) (Right _) = Left (Just e, Nothing)
@@ -165,7 +143,7 @@
 
   -- Add errors to forms with zero rows if the form is not a FormMissing
   result :: [(Text, Text, Either (Maybe Text, Maybe Text) Posting)]
-  result = case (acctRes, amtRes) of
+  result = case (acctsRes, amtsRes) of
     (FormMissing, FormMissing) -> postings
     _ -> case postings of
       [] -> [ ("", "", Left (Just "Missing account", Just "Missing amount"))
@@ -187,6 +165,30 @@
 
   in (formResult, zip [(1 :: Int)..] display)
 
+-- helper for add-form.hamlet
+toBloodhoundJson :: [Text] -> Markup
+toBloodhoundJson ts =
+  -- This used to work, but since 1.16, it seems like something changed.
+  -- toJSON ("a"::Text) gives String "a" instead of "a", etc.
+  -- preEscapedString . escapeJSSpecialChars . show . toJSON
+
+  preEscapedText $ T.concat [
+    "[",
+    T.intercalate "," $ map (
+      ("{\"value\":" <>).
+      (<> "}").
+      -- This will convert a value such as ``hledger!`` into
+      -- ``atob("aGxlZGdlciE=")``. When this gets evaluated on the client,
+      -- the resulting string is ``hledger!`` again. The same data is
+      -- passed, but the user-controlled bit of that string can only use
+      -- characters [a-zA-Z0-9+=/], making it impossible to break out of
+      -- string context.
+      b64wrap
+      ) ts,
+    "]"
+    ]
+  where
+    b64wrap = ("atob(\""<>) . (<>"\")") . encodeBase64
 
 zipDefault :: a -> [a] -> [a] -> [(a, a)]
 zipDefault def (b:bs) (c:cs) = (b, c):(zipDefault def bs cs)
diff --git a/Hledger/Web/Widget/Common.hs b/Hledger/Web/Widget/Common.hs
--- a/Hledger/Web/Widget/Common.hs
+++ b/Hledger/Web/Widget/Common.hs
@@ -118,14 +118,14 @@
     map quoteIfSpaced .
     filter (\term ->
         not $ T.isPrefixOf "date:" term || T.isPrefixOf "date2:" term) .
-    Query.words'' Query.prefixes
+    Query.words'' queryprefixes
 
 removeInacct :: Text -> [Text]
 removeInacct =
     map quoteIfSpaced .
     filter (\term ->
         not $ T.isPrefixOf "inacct:" term || T.isPrefixOf "inacctonly:" term) .
-    Query.words'' Query.prefixes
+    Query.words'' queryprefixes
 
 replaceInacct :: Text -> Text -> Text
 replaceInacct q acct = T.unwords $ acct : removeInacct q
diff --git a/hledger-web.1 b/hledger-web.1
--- a/hledger-web.1
+++ b/hledger-web.1
@@ -1,12 +1,12 @@
 
-.TH "HLEDGER-WEB" "1" "July 2022" "hledger-web-1.26.1 " "hledger User Manuals"
+.TH "HLEDGER-WEB" "1" "September 2022" "hledger-web-1.27 " "hledger User Manuals"
 
 
 
 .SH NAME
 .PP
 hledger-web is a web interface (WUI) for the hledger accounting tool.
-This manual is for hledger-web 1.26.1.
+This manual is for hledger-web 1.27.
 .SH SYNOPSIS
 .PP
 \f[C]hledger-web [OPTIONS]\f[R]
@@ -615,9 +615,10 @@
 reboot.
 .PP
 On Windows computers, the default value is probably
-\f[C]C:\[rs]Users\[rs]MyUserName\[rs].hledger.journal\f[R].
+\f[C]C:\[rs]Users\[rs]YOURNAME\[rs].hledger.journal\f[R].
 You can change this by running a command like this in a powershell
-window:
+window (let us know if you need to be an Administrator, and if this
+persists across a reboot):
 .IP
 .nf
 \f[C]
@@ -625,8 +626,8 @@
 \f[R]
 .fi
 .PP
-(Let us know if you need to be an Administrator, and if this persists
-across a reboot.)
+Or, change it in settings: see
+https://www.java.com/en/download/help/path.html.
 .SH FILES
 .PP
 Reads data from one or more files in hledger journal, timeclock,
diff --git a/hledger-web.cabal b/hledger-web.cabal
--- a/hledger-web.cabal
+++ b/hledger-web.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           hledger-web
-version:        1.26.1
+version:        1.27
 synopsis:       Web-based user interface for the hledger accounting system
 description:    A simple web-based user interface for the hledger accounting system,
                 providing a more modern UI than the command-line or terminal interfaces.
@@ -28,7 +28,7 @@
 license-file:   LICENSE
 build-type:     Simple
 tested-with:
-    GHC==8.8.4, GHC==8.10.4, GHC==9.0.1
+    GHC==8.10.7, GHC==9.0.2, GHC==9.2.4
 extra-source-files:
     CHANGES.md
     README.md
@@ -151,14 +151,15 @@
   hs-source-dirs:
       ./
   ghc-options: -Wall -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns
-  cpp-options: -DVERSION="1.26.1"
+  cpp-options: -DVERSION="1.27"
   build-depends:
       Decimal >=0.5.1
     , aeson >=1
-    , base >=4.11 && <4.17
+    , base >=4.14 && <4.17
     , base64
     , blaze-html
     , blaze-markup
+    , breakpoint
     , bytestring
     , case-insensitive
     , clientsession
@@ -171,8 +172,8 @@
     , extra >=1.6.3
     , filepath
     , hjsmin
-    , hledger >=1.26.1 && <1.27
-    , hledger-lib >=1.26.1 && <1.27
+    , hledger ==1.27.*
+    , hledger-lib ==1.27.*
     , hspec
     , http-client
     , http-conduit
@@ -212,9 +213,10 @@
   hs-source-dirs:
       app
   ghc-options: -Wall -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns
-  cpp-options: -DVERSION="1.26.1"
+  cpp-options: -DVERSION="1.27"
   build-depends:
-      base
+      base >=4.14 && <4.17
+    , breakpoint
     , hledger-web
   if (flag(dev)) || (flag(library-only))
     cpp-options: -DDEVELOPMENT
@@ -232,9 +234,10 @@
   hs-source-dirs:
       test
   ghc-options: -Wall -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns
-  cpp-options: -DVERSION="1.26.1"
+  cpp-options: -DVERSION="1.27"
   build-depends:
-      base
+      base >=4.14 && <4.17
+    , breakpoint
     , hledger
     , hledger-lib
     , hledger-web
diff --git a/hledger-web.info b/hledger-web.info
--- a/hledger-web.info
+++ b/hledger-web.info
@@ -12,7 +12,7 @@
 **************
 
 hledger-web is a web interface (WUI) for the hledger accounting tool.
-This manual is for hledger-web 1.26.1.
+This manual is for hledger-web 1.27.
 
    'hledger-web [OPTIONS]'
 'hledger web -- [OPTIONS]'
@@ -592,13 +592,14 @@
    For this to take effect you might need to 'killall Dock', or reboot.
 
    On Windows computers, the default value is probably
-'C:\Users\MyUserName\.hledger.journal'.  You can change this by running
-a command like this in a powershell window:
+'C:\Users\YOURNAME\.hledger.journal'.  You can change this by running a
+command like this in a powershell window (let us know if you need to be
+an Administrator, and if this persists across a reboot):
 
 > setx LEDGER_FILE "C:\Users\MyUserName\finance\2021.journal"
 
-   (Let us know if you need to be an Administrator, and if this persists
-across a reboot.)
+   Or, change it in settings: see
+https://www.java.com/en/download/help/path.html.
 
 
 File: hledger-web.info,  Node: FILES,  Next: BUGS,  Prev: ENVIRONMENT,  Up: Top
@@ -631,22 +632,22 @@
 
 Tag Table:
 Node: Top223
-Node: OPTIONS1888
-Ref: #options1993
-Node: PERMISSIONS9904
-Ref: #permissions10043
-Node: EDITING UPLOADING DOWNLOADING11255
-Ref: #editing-uploading-downloading11436
-Node: RELOADING12270
-Ref: #reloading12404
-Node: JSON API12837
-Ref: #json-api12951
-Node: ENVIRONMENT18441
-Ref: #environment18557
-Node: FILES19791
-Ref: #files19891
-Node: BUGS20104
-Ref: #bugs20182
+Node: OPTIONS1886
+Ref: #options1991
+Node: PERMISSIONS9902
+Ref: #permissions10041
+Node: EDITING UPLOADING DOWNLOADING11253
+Ref: #editing-uploading-downloading11434
+Node: RELOADING12268
+Ref: #reloading12402
+Node: JSON API12835
+Ref: #json-api12949
+Node: ENVIRONMENT18439
+Ref: #environment18555
+Node: FILES19866
+Ref: #files19966
+Node: BUGS20179
+Ref: #bugs20257
 
 End Tag Table
 
diff --git a/hledger-web.txt b/hledger-web.txt
--- a/hledger-web.txt
+++ b/hledger-web.txt
@@ -5,7 +5,7 @@
 
 NAME
        hledger-web  is  a web interface (WUI) for the hledger accounting tool.
-       This manual is for hledger-web 1.26.1.
+       This manual is for hledger-web 1.27.
 
 SYNOPSIS
        hledger-web [OPTIONS]
@@ -537,23 +537,24 @@
 
        For this to take effect you might need to killall Dock, or reboot.
 
-       On Windows computers, the default value  is  probably  C:\Users\MyUser-
-       Name\.hledger.journal.   You  can change this by running a command like
-       this in a powershell window:
+       On Windows computers, the  default  value  is  probably  C:\Users\YOUR-
+       NAME\.hledger.journal.   You  can change this by running a command like
+       this in a powershell window (let us know if you need to be an  Adminis-
+       trator, and if this persists across a reboot):
 
               > setx LEDGER_FILE "C:\Users\MyUserName\finance\2021.journal"
 
-       (Let us know if you need to be an Administrator, and if  this  persists
-       across a reboot.)
+       Or,   change   it   in   settings:   see  https://www.java.com/en/down-
+       load/help/path.html.
 
 FILES
-       Reads  data from one or more files in hledger journal, timeclock, time-
-       dot,  or  CSV  format  specified   with   -f,   or   $LEDGER_FILE,   or
-       $HOME/.hledger.journal           (on          windows,          perhaps
+       Reads data from one or more files in hledger journal, timeclock,  time-
+       dot,   or   CSV   format   specified   with  -f,  or  $LEDGER_FILE,  or
+       $HOME/.hledger.journal          (on          windows,           perhaps
        C:/Users/USER/.hledger.journal).
 
 BUGS
-       The need to precede options with -- when invoked from hledger  is  awk-
+       The  need  to precede options with -- when invoked from hledger is awk-
        ward.
 
        -f- doesn't work (hledger-web can't read from stdin).
@@ -567,7 +568,7 @@
 
 
 REPORTING BUGS
-       Report  bugs at http://bugs.hledger.org (or on the #hledger IRC channel
+       Report bugs at http://bugs.hledger.org (or on the #hledger IRC  channel
        or hledger mail list)
 
 
@@ -585,4 +586,4 @@
 
 
 
-hledger-web-1.26.1                 July 2022                    HLEDGER-WEB(1)
+hledger-web-1.27                September 2022                  HLEDGER-WEB(1)
diff --git a/static/hledger.js b/static/hledger.js
--- a/static/hledger.js
+++ b/static/hledger.js
@@ -4,16 +4,20 @@
 // STARTUP
 
 $(document).ready(function() {
+
+  // add form helpers XXX move to addForm ?
+
   // date picker
   // http://bootstrap-datepicker.readthedocs.io/en/latest/options.html
   var dateEl = $('#dateWrap').datepicker({
     showOnFocus: false,
     autoclose: true,
     format: 'yyyy-mm-dd',
+    todayHighlight: true,
     weekStart: 1 // Monday
   });;
 
-  // ensure add form always focuses its first field
+  // focus and pre-fill the add form whenever it is shown
   $('#addmodal')
     .on('shown.bs.modal', function() {
       addformFocus();
@@ -163,9 +167,14 @@
   $('.amount-input:last').keypress(addformAddPosting);
 }
 
-// Focus the first add form field.
+// Pre-fill today's date and focus the description field in the add form.
 function addformFocus() {
-  focus($('#addform input#date'));
+  $('#addform input[name=date]').val(isoDate());
+  focus($('#addform input[name=description]'));
+}
+
+function isoDate() {
+  return new Date().toLocaleDateString("sv");  // https://stackoverflow.com/a/58633651/84401
 }
 
 // Focus a jquery-wrapped element, working around http://stackoverflow.com/a/7046837.
diff --git a/templates/add-form.hamlet b/templates/add-form.hamlet
--- a/templates/add-form.hamlet
+++ b/templates/add-form.hamlet
@@ -21,47 +21,42 @@
   });
 ^{extra}
 
-<div .form-group>
-  <div .row>
-    <div .col-md-3 .col-xs-6 .col-sm-6 :isJust (fvErrors dateView):.has-error>
-      <div #dateWrap .form-group.input-group.date>
-        ^{fvInput dateView}
-        <div .input-group-addon>
-          <span .glyphicon .glyphicon-th>
-      $maybe err <- fvErrors dateView
-        <span .help-block .error-block>#{err}
-    <div .col-md-9 .col-xs-6 .col-sm-6 :isJust (fvErrors descView):.has-error>
-      <div .form-group>
-        ^{fvInput descView}
-      $maybe err <- fvErrors descView
-        <span .help-block .error-block>#{err}
-  <div .row>
-    <div .col-md-3 .col-xs-6 .col-sm-6>
-    <div .col-md-9 .col-xs-6 .col-sm-6>
+<div .row>
+  <div .col-md-3.col-sm-5.col-xs-6 :isJust (fvErrors dateView):.has-error>
+    <div #dateWrap .form-group.input-group.date>
+      ^{fvInput dateView}
+      <div .input-group-addon>
+        <span .glyphicon .glyphicon-th>
+    $maybe err <- fvErrors dateView
+      <span .help-block .error-block>#{err}
+  <div .col-md-9.col-sm-7.col-xs-6 :isJust (fvErrors descView):.has-error>
+    <div .form-group>
+      ^{fvInput descView}
+    $maybe err <- fvErrors descView
+      <span .help-block .error-block>#{err}
 
 <div .account-postings>
   $forall (n, (acc, amt, accE, amtE)) <- displayRows
-    <div .form-group .row .account-group>
-      <div .col-md-8 .col-xs-8 .col-sm-8 :isJust accE:.has-error>
+    <div .form-group .row .account-group style="padding-left:1em;">
+      <div .col-sm-9.col-xs-9 :isJust accE:.has-error>
         <input .account-input.form-control.input-lg.typeahead type=text
           name=account placeholder="Account #{n}" value="#{acc}">
         $maybe err <- accE
           <span .help-block .error-block>_{err}
-      <div .col-md-4 .col-xs-4 .col-sm-4 :isJust amtE:.has-error>
+      <div .col-sm-3.col-xs-3 :isJust amtE:.has-error>
         <input .amount-input.form-control.input-lg type=text
           name=amount placeholder="Amount #{n}" value="#{amt}">
         $maybe err <- amtE
           <span .help-block .error-block>_{err}
 
 <div .row>
-  <div .col-md-8 .col-xs-8 .col-sm-8>
-  <div .col-md-4 .col-xs-4 .col-sm-4>
-    <button type=submit .btn .btn-default .btn-lg name=submit>add
-
-$if length journals > 1
-  <br>
-  <span .input-lg>to:
-    <select #journalselect .form-control.input-lg name=journal style="width:auto; display:inline-block;">
-      $forall p <- journals
-        <option value=#{p}>#{p}
-<span .small style="padding-left:2em;">
+  <div .col-sm-9.col-xs-9>
+    $if length files > 1
+      Add to:
+      &nbsp;
+      <div style="display:inline-block; width:auto;" .form-group :isJust (fvErrors fileView):.has-error>
+        ^{fvInput fileView}
+        $maybe err <- fvErrors fileView
+          <span .help-block .error-block>#{err}
+  <div .col-sm-3.col-xs-3 style="text-align:right;">
+    <button type=submit .btn .btn-default .btn-lg name=submit style="font-weight:bold;">Save
diff --git a/templates/manage.hamlet b/templates/manage.hamlet
--- a/templates/manage.hamlet
+++ b/templates/manage.hamlet
@@ -2,7 +2,7 @@
   Your journal's files
 
 <div.row>
-  <div .col-xs-12.col-sm-8.col-md-6>
+  <div .col-md-6.col-sm-8.col-xs-12>
     <table .table.table-condensed>
       <thead>
         <th>
