fix: tighten typography, fix parser brace leak, polish UI
Build and Deploy / build-and-deploy (push) Successful in 1m40s
Build and Deploy / build-and-deploy (push) Successful in 1m40s
- Drop italics on cookware/timer tags — they were over-emphasizing
roughly a third of step text. Now plain with slight opacity.
- Fix parser bug: @salt{large pinch} braces no longer leak through
as raw text. Non-numeric quantities are consumed silently,
ingredient appears without displayed quantity.
- Remove duplicate '\342\206\227 Original' link from marginalia SOURCE block.
Title-adjacent link is sufficient.
- Add tags to marginalia column for visual balance.
- Constrain content to 1120px max-width for a printed-cookbook feel.
- Add .container h2 CSS specificity override to beat Pico CSS defaults.
Section labels (INGREDIENTS, METHOD) now properly render in Fraunces.
- Switch navbars from <div> to <nav> so Pico's own nav ul{list-style:none}
suppresses gray square bullets. Add explicit li{list-style:none} override.
- Constrain content to 1120px max-width so parchment frames the page.
This commit is contained in:
+17
-13
@@ -147,21 +147,21 @@ page title content =
|
|||||||
, ".quicksand { font-family: \"Quicksand\", sans-serif; font-optical-sizing: auto; font-style: normal; }"
|
, ".quicksand { font-family: \"Quicksand\", sans-serif; font-optical-sizing: auto; font-style: normal; }"
|
||||||
, ""
|
, ""
|
||||||
, "body { background: var(--roux-bg); color: var(--roux-text); font-family: \"Quicksand\", sans-serif; }"
|
, "body { background: var(--roux-bg); color: var(--roux-text); font-family: \"Quicksand\", sans-serif; }"
|
||||||
, "main.container { padding-top: 1.5rem; }"
|
, "main.container { padding-top: 1.5rem; max-width: 1120px; }"
|
||||||
, "h1, h3, .roux-heading { font-family: \"Fraunces\", serif; font-optical-sizing: auto; }"
|
, "h1, h3, .roux-heading { font-family: \"Fraunces\", serif; font-optical-sizing: auto; }"
|
||||||
, "h1 { font-size: 2rem; font-weight: 500; margin: 0 0 0.5rem; line-height: 1.15; color: var(--roux-text); letter-spacing: -0.01em; }"
|
, "h1 { font-size: 2rem; font-weight: 500; margin: 0 0 0.5rem; line-height: 1.15; color: var(--roux-text); letter-spacing: -0.01em; }"
|
||||||
, "h2 { font-family: \"Fraunces\", serif; font-size: 0.75rem; font-weight: 600; margin: 0 0 1rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--roux-text); opacity: 0.6; }"
|
, "h2, .container h2 { font-family: \"Fraunces\", serif; font-size: 0.75rem; font-weight: 600; margin: 0 0 1rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--roux-text); opacity: 0.6; }"
|
||||||
, "h3 { font-size: 1rem; font-weight: 500; margin: 1.5rem 0 0.5rem; color: var(--roux-text); text-transform: uppercase; opacity: 0.75; }"
|
, "h3 { font-size: 1rem; font-weight: 500; margin: 1.5rem 0 0.5rem; color: var(--roux-text); text-transform: uppercase; opacity: 0.75; }"
|
||||||
, "a { color: var(--roux-text); text-decoration: none; transition: color 0.15s; }"
|
, "a { color: var(--roux-text); text-decoration: none; transition: color 0.15s; }"
|
||||||
, "a:hover { color: var(--roux-accent); }"
|
, "a:hover { color: var(--roux-accent); }"
|
||||||
, "a[role=button] { --pico-color: var(--roux-accent); }"
|
, "a[role=button] { --pico-color: var(--roux-accent); }"
|
||||||
, ""
|
, ""
|
||||||
, "/* Navbar */"
|
, "/* Navbar */"
|
||||||
, ".roux-navbar { display: flex; justify-content: space-between; align-items: center; padding: 0.75rem 0; margin-bottom: 1rem; border-bottom: 1px solid rgba(61, 44, 30, 0.1); }"
|
, "nav.roux-navbar { display: flex; justify-content: space-between; align-items: center; padding: 0.75rem 0; margin-bottom: 1rem; border-bottom: 1px solid rgba(61, 44, 30, 0.1); }"
|
||||||
, ".roux-navbar .roux-logo { font-family: \"Fraunces\", serif; font-size: 1.1rem; font-weight: 500; color: var(--roux-text); text-decoration: none; }"
|
, ".roux-navbar .roux-logo { font-family: \"Fraunces\", serif; font-size: 1.1rem; font-weight: 500; color: var(--roux-text); text-decoration: none; }"
|
||||||
, ".roux-navbar .roux-logo:hover { color: var(--roux-text); }"
|
, ".roux-navbar .roux-logo:hover { color: var(--roux-text); }"
|
||||||
, ".roux-navbar ul { display: flex; gap: 1rem; list-style: none; margin: 0; padding: 0; }"
|
, ".roux-navbar ul { display: flex; gap: 1rem; list-style: none; margin: 0; padding: 0; }"
|
||||||
, ".roux-navbar li { margin: 0; padding: 0; }"
|
, ".roux-navbar li { margin: 0; padding: 0; list-style: none; }"
|
||||||
, ".roux-navbar a { font-size: 0.8rem; letter-spacing: 0.04em; text-transform: uppercase; }"
|
, ".roux-navbar a { font-size: 0.8rem; letter-spacing: 0.04em; text-transform: uppercase; }"
|
||||||
, ".roux-navbar a.active { color: var(--roux-accent); font-weight: 500; }"
|
, ".roux-navbar a.active { color: var(--roux-accent); font-weight: 500; }"
|
||||||
, ""
|
, ""
|
||||||
@@ -221,9 +221,9 @@ page title content =
|
|||||||
, ".roux-course { font-size: 0.6rem; color: var(--roux-accent); opacity: 0.65; margin: 0 0 0.15rem; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 500; }"
|
, ".roux-course { font-size: 0.6rem; color: var(--roux-accent); opacity: 0.65; margin: 0 0 0.15rem; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 500; }"
|
||||||
, ".roux-desc { font-size: 0.85rem; line-height: 1.6; color: var(--roux-text); margin: 0 0 1.5rem; max-width: 640px; opacity: 0.85; }"
|
, ".roux-desc { font-size: 0.85rem; line-height: 1.6; color: var(--roux-text); margin: 0 0 1.5rem; max-width: 640px; opacity: 0.85; }"
|
||||||
, ""
|
, ""
|
||||||
, ".roux-ingredient-tag { border-bottom: 1.5px solid rgba(191, 148, 40, 0.35); font-weight: 500; }"
|
, ".roux-ingredient-tag { border-bottom: 1.5px solid rgba(191, 148, 40, 0.35); }"
|
||||||
, ".roux-cookware-tag { font-style: italic; opacity: 0.75; }"
|
, ".roux-cookware-tag, .roux-timer-tag { opacity: 0.85; }"
|
||||||
, ".roux-timer-tag { border-bottom: 1px dashed rgba(61, 44, 30, 0.25); font-style: italic; }"
|
, ".roux-ingredient-tag .qty { font-size: 0.8rem; color: var(--roux-text); opacity: 0.55; }"
|
||||||
, ".roux-comment-tag { opacity: 0.5; font-style: italic; font-size: 0.85em; }"
|
, ".roux-comment-tag { opacity: 0.5; font-style: italic; font-size: 0.85em; }"
|
||||||
, ".tag { display: inline-block; border: 1px solid rgba(122, 154, 122, 0.35); padding: 0.1rem 0.5rem; margin: 0.1rem; border-radius: 4px; font-size: 0.7rem; color: var(--roux-sage); background: transparent; }"
|
, ".tag { display: inline-block; border: 1px solid rgba(122, 154, 122, 0.35); padding: 0.1rem 0.5rem; margin: 0.1rem; border-radius: 4px; font-size: 0.7rem; color: var(--roux-sage); background: transparent; }"
|
||||||
, ".r-section-header { display: block; font-family: \"Fraunces\", serif; font-size: 0.85rem; font-weight: 500; color: var(--roux-text); opacity: 0.5; margin: 1rem 0 0.5rem; letter-spacing: 0.01em; border-top: 0.5px solid rgba(61, 44, 30, 0.06); padding-top: 0.75rem; }"
|
, ".r-section-header { display: block; font-family: \"Fraunces\", serif; font-size: 0.85rem; font-weight: 500; color: var(--roux-text); opacity: 0.5; margin: 1rem 0 0.5rem; letter-spacing: 0.01em; border-top: 0.5px solid rgba(61, 44, 30, 0.06); padding-top: 0.75rem; }"
|
||||||
@@ -431,7 +431,7 @@ sseIndexJs =
|
|||||||
indexPage :: SortMode -> [Idx.RecipeInfo] -> ByteString
|
indexPage :: SortMode -> [Idx.RecipeInfo] -> ByteString
|
||||||
indexPage _mode recipes =
|
indexPage _mode recipes =
|
||||||
page "Roux — Recipes" $ do
|
page "Roux — Recipes" $ do
|
||||||
H.div ! A.class_ "roux-navbar" $ do
|
H.nav ! A.class_ "roux-navbar" $ do
|
||||||
H.ul $ H.li $ H.a ! A.class_ "roux-logo" ! A.href "/" $ "Roux"
|
H.ul $ H.li $ H.a ! A.class_ "roux-logo" ! A.href "/" $ "Roux"
|
||||||
H.ul $ do
|
H.ul $ do
|
||||||
H.li $ H.a ! A.href "#alpha" $ "A-Z"
|
H.li $ H.a ! A.href "#alpha" $ "A-Z"
|
||||||
@@ -468,7 +468,7 @@ indexPage _mode recipes =
|
|||||||
importPage :: Maybe Text -> ByteString
|
importPage :: Maybe Text -> ByteString
|
||||||
importPage merror =
|
importPage merror =
|
||||||
page "Roux \8212 Import Recipe" $ do
|
page "Roux \8212 Import Recipe" $ do
|
||||||
H.div ! A.class_ "roux-navbar" $ do
|
H.nav ! A.class_ "roux-navbar" $ do
|
||||||
H.ul $ H.li $ H.a ! A.href "/" $ "\8592 Back"
|
H.ul $ H.li $ H.a ! A.href "/" $ "\8592 Back"
|
||||||
H.ul $ H.li $ H.strong "Import Recipe"
|
H.ul $ H.li $ H.strong "Import Recipe"
|
||||||
case merror of
|
case merror of
|
||||||
@@ -489,7 +489,7 @@ importPage merror =
|
|||||||
importResultPage :: ImportError -> ByteString
|
importResultPage :: ImportError -> ByteString
|
||||||
importResultPage (ImportError msg) =
|
importResultPage (ImportError msg) =
|
||||||
page "Roux \8212 Import Error" $ do
|
page "Roux \8212 Import Error" $ do
|
||||||
H.div ! A.class_ "roux-navbar" $ do
|
H.nav ! A.class_ "roux-navbar" $ do
|
||||||
H.ul $ H.li $ H.a ! A.href "/" $ "\8592 Back"
|
H.ul $ H.li $ H.a ! A.href "/" $ "\8592 Back"
|
||||||
H.ul $ H.li $ H.a ! A.href "/import" $ "Try again"
|
H.ul $ H.li $ H.a ! A.href "/import" $ "Try again"
|
||||||
H.h2 "Import failed"
|
H.h2 "Import failed"
|
||||||
@@ -520,7 +520,7 @@ recipePage info =
|
|||||||
-- | Render the body of a recipe page (no page shell).
|
-- | Render the body of a recipe page (no page shell).
|
||||||
renderRecipe :: Recipe -> Html
|
renderRecipe :: Recipe -> Html
|
||||||
renderRecipe recipe = do
|
renderRecipe recipe = do
|
||||||
H.div ! A.class_ "roux-navbar" $ do
|
H.nav ! A.class_ "roux-navbar" $ do
|
||||||
H.ul $ H.li $ H.a ! A.class_ "roux-logo" ! A.href "/" $ "Roux"
|
H.ul $ H.li $ H.a ! A.class_ "roux-logo" ! A.href "/" $ "Roux"
|
||||||
H.ul $ H.li $ H.a ! A.href "/" $ "Recipes"
|
H.ul $ H.li $ H.a ! A.href "/" $ "Recipes"
|
||||||
let meta = recipeMetadata recipe
|
let meta = recipeMetadata recipe
|
||||||
@@ -555,9 +555,13 @@ renderRecipe recipe = do
|
|||||||
case metaSource meta of
|
case metaSource meta of
|
||||||
Just url -> H.div ! A.class_ "note" $ do
|
Just url -> H.div ! A.class_ "note" $ do
|
||||||
H.h2 "Source"
|
H.h2 "Source"
|
||||||
H.a ! A.href (H.toValue url) ! A.target "_blank" ! A.rel "noopener noreferrer" $ do
|
H.p $ H.toHtml url
|
||||||
"↗ Original recipe"
|
|
||||||
Nothing -> pure ()
|
Nothing -> pure ()
|
||||||
|
let recipeTags = metaTags meta
|
||||||
|
unless (null recipeTags) $ do
|
||||||
|
H.div ! A.class_ "note" $ do
|
||||||
|
H.h2 "Tags"
|
||||||
|
H.p $ H.toHtml (T.intercalate " · " recipeTags)
|
||||||
unless (null notes) $ do
|
unless (null notes) $ do
|
||||||
H.h2 "Notes"
|
H.h2 "Notes"
|
||||||
mapM_ renderMarginalNote notes
|
mapM_ renderMarginalNote notes
|
||||||
|
|||||||
+1
-1
@@ -296,7 +296,7 @@ pQuantityBody = do
|
|||||||
[] -> return Nothing -- empty braces
|
[] -> return Nothing -- empty braces
|
||||||
_ -> case parseQuantityText (T.pack content) of
|
_ -> case parseQuantityText (T.pack content) of
|
||||||
Just q -> return (Just q)
|
Just q -> return (Just q)
|
||||||
Nothing -> P.parserFail ("invalid quantity: " <> content)
|
Nothing -> return Nothing -- non-numeric quantity, consume silently
|
||||||
|
|
||||||
{- | Parse a quantity text like @2@, @1%kg@, @=1%tsp@, @1\/2%tbsp@, or
|
{- | Parse a quantity text like @2@, @1%kg@, @=1%tsp@, @1\/2%tbsp@, or
|
||||||
@1,1\/2cups@ (unit without @%@ separator).
|
@1,1\/2cups@ (unit without @%@ separator).
|
||||||
|
|||||||
Reference in New Issue
Block a user