summaryrefslogtreecommitdiff
path: root/Omni/Jr/Web/Style.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Jr/Web/Style.hs')
-rw-r--r--Omni/Jr/Web/Style.hs510
1 files changed, 510 insertions, 0 deletions
diff --git a/Omni/Jr/Web/Style.hs b/Omni/Jr/Web/Style.hs
new file mode 100644
index 0000000..e2377b5
--- /dev/null
+++ b/Omni/Jr/Web/Style.hs
@@ -0,0 +1,510 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+-- : dep clay
+module Omni.Jr.Web.Style
+ ( css,
+ statusBadgeClass,
+ priorityBadgeClass,
+ )
+where
+
+import Alpha hiding (wrap, (**), (|>))
+import Clay
+import qualified Clay.Flexbox as Flexbox
+import qualified Clay.Media as Media
+import qualified Clay.Stylesheet as Stylesheet
+import qualified Data.List.NonEmpty as NE
+import qualified Data.Text.Lazy as LazyText
+
+css :: LazyText.Text
+css = render stylesheet
+
+stylesheet :: Css
+stylesheet = do
+ baseStyles
+ layoutStyles
+ navigationStyles
+ cardStyles
+ statusBadges
+ buttonStyles
+ formStyles
+ responsiveStyles
+ darkModeStyles
+
+baseStyles :: Css
+baseStyles = do
+ star ? boxSizing borderBox
+ html <> body ? do
+ margin (px 0) (px 0) (px 0) (px 0)
+ padding (px 0) (px 0) (px 0) (px 0)
+ body ? do
+ fontFamily
+ [ "-apple-system",
+ "BlinkMacSystemFont",
+ "Segoe UI",
+ "Roboto",
+ "Helvetica Neue",
+ "Arial",
+ "Noto Sans",
+ "sans-serif"
+ ]
+ [sansSerif]
+ fontSize (px 16)
+ lineHeight (em 1.5)
+ color "#1f2937"
+ backgroundColor "#f3f4f6"
+ minHeight (vh 100)
+ "h1" ? do
+ fontSize (px 24)
+ fontWeight bold
+ margin (px 0) (px 0) (em 0.5) (px 0)
+ "h2" ? do
+ fontSize (px 18)
+ fontWeight (weight 600)
+ color "#374151"
+ margin (em 1.5) (px 0) (em 0.75) (px 0)
+ "h3" ? do
+ fontSize (px 16)
+ fontWeight (weight 600)
+ color "#374151"
+ margin (em 1) (px 0) (em 0.5) (px 0)
+ a ? do
+ color "#0066cc"
+ textDecoration none
+ a # hover ? textDecoration underline
+ code ? do
+ fontFamily ["SF Mono", "Monaco", "Consolas", "monospace"] [monospace]
+ fontSize (em 0.9)
+ backgroundColor "#f3f4f6"
+ padding (px 2) (px 6) (px 2) (px 6)
+ borderRadius (px 4) (px 4) (px 4) (px 4)
+ pre ? do
+ fontFamily ["SF Mono", "Monaco", "Consolas", "monospace"] [monospace]
+ fontSize (px 13)
+ backgroundColor "#1e1e1e"
+ color "#d4d4d4"
+ padding (px 16) (px 16) (px 16) (px 16)
+ borderRadius (px 6) (px 6) (px 6) (px 6)
+ overflow auto
+ whiteSpace preWrap
+ maxHeight (px 500)
+
+layoutStyles :: Css
+layoutStyles = do
+ ".container" ? do
+ width (pct 100)
+ maxWidth (px 900)
+ margin (px 0) auto (px 0) auto
+ padding (px 16) (px 16) (px 16) (px 16)
+ main_ ? do
+ Stylesheet.key "flex" ("1 0 auto" :: Text)
+ ".page-content" ? do
+ padding (px 0) (px 0) (px 0) (px 0)
+ ".stats-grid" ? do
+ display grid
+ Stylesheet.key "grid-template-columns" ("repeat(auto-fit, minmax(100px, 1fr))" :: Text)
+ Stylesheet.key "gap" ("12px" :: Text)
+ ".task-list" ? do
+ display flex
+ flexDirection column
+ Stylesheet.key "gap" ("8px" :: Text)
+ ".detail-row" ? do
+ display flex
+ flexWrap Flexbox.wrap
+ padding (px 10) (px 0) (px 10) (px 0)
+ borderBottom (px 1) solid "#e5e7eb"
+ ".detail-row" # lastChild ? borderBottom (px 0) none transparent
+ ".detail-label" ? do
+ fontWeight (weight 600)
+ width (px 120)
+ color "#6b7280"
+ minWidth (px 100)
+ ".detail-value" ? do
+ Stylesheet.key "flex" ("1" :: Text)
+ minWidth (px 0)
+ ".detail-section" ? do
+ marginTop (em 1)
+ paddingTop (em 1)
+ borderTop (px 1) solid "#e5e7eb"
+ ".dep-list" <> ".child-list" ? do
+ margin (px 8) (px 0) (px 8) (px 0)
+ paddingLeft (px 24)
+ (".dep-list" ** li) <> (".child-list" ** li) ? margin (px 6) (px 0) (px 6) (px 0)
+ ".dep-type" <> ".child-status" ? do
+ color "#6b7280"
+ fontSize (px 13)
+ ".child-title" ? color "#374151"
+ ".priority-desc" ? do
+ color "#6b7280"
+ marginLeft (px 4)
+
+navigationStyles :: Css
+navigationStyles = do
+ header ? do
+ backgroundColor white
+ padding (px 12) (px 16) (px 12) (px 16)
+ boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.08) (shadow (px 0) (px 2))))
+ marginBottom (px 16)
+ ".nav-content" ? do
+ maxWidth (px 900)
+ margin (px 0) auto (px 0) auto
+ display flex
+ alignItems center
+ justifyContent spaceBetween
+ flexWrap Flexbox.wrap
+ Stylesheet.key "gap" ("12px" :: Text)
+ ".nav-brand" ? do
+ fontSize (px 20)
+ fontWeight bold
+ color "#1f2937"
+ textDecoration none
+ ".nav-brand" # hover ? textDecoration none
+ ".nav-links" ? do
+ display flex
+ Stylesheet.key "gap" ("8px" :: Text)
+ flexWrap Flexbox.wrap
+ ".back-link" ? do
+ display inlineBlock
+ marginBottom (em 0.75)
+ fontSize (px 14)
+ ".actions" ? do
+ display flex
+ flexWrap Flexbox.wrap
+ Stylesheet.key "gap" ("8px" :: Text)
+ marginBottom (px 16)
+
+cardStyles :: Css
+cardStyles = do
+ ".card"
+ <> ".task-card"
+ <> ".stat-card"
+ <> ".task-detail"
+ <> ".task-summary"
+ <> ".filter-form"
+ <> ".status-form"
+ <> ".diff-section"
+ <> ".review-actions"
+ ? do
+ backgroundColor white
+ borderRadius (px 8) (px 8) (px 8) (px 8)
+ padding (px 16) (px 16) (px 16) (px 16)
+ boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.1) (shadow (px 0) (px 1))))
+ ".stat-card" ? textAlign center
+ ".stat-count" ? do
+ fontSize (px 28)
+ fontWeight bold
+ ".stat-label" ? do
+ fontSize (px 12)
+ color "#6b7280"
+ marginTop (px 4)
+ ".stat-card.badge-open" ? do
+ borderLeft (px 4) solid "#f59e0b"
+ (".stat-card.badge-open" |> ".stat-count") ? color "#92400e"
+ ".stat-card.badge-inprogress" ? borderLeft (px 4) solid "#3b82f6"
+ (".stat-card.badge-inprogress" |> ".stat-count") ? color "#1e40af"
+ ".stat-card.badge-review" ? borderLeft (px 4) solid "#8b5cf6"
+ (".stat-card.badge-review" |> ".stat-count") ? color "#6b21a8"
+ ".stat-card.badge-approved" ? borderLeft (px 4) solid "#06b6d4"
+ (".stat-card.badge-approved" |> ".stat-count") ? color "#0e7490"
+ ".stat-card.badge-done" ? borderLeft (px 4) solid "#10b981"
+ (".stat-card.badge-done" |> ".stat-count") ? color "#065f46"
+ ".task-card" ? do
+ transition "box-shadow" (ms 150) ease (sec 0)
+ ".task-card" # hover ? do
+ boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.15) (shadow (px 0) (px 4))))
+ ".task-header" ? do
+ display flex
+ flexWrap Flexbox.wrap
+ alignItems center
+ Stylesheet.key "gap" ("8px" :: Text)
+ marginBottom (px 8)
+ ".task-id" ? do
+ fontFamily ["SF Mono", "Monaco", "monospace"] [monospace]
+ color "#0066cc"
+ textDecoration none
+ fontSize (px 14)
+ padding (px 4) (px 0) (px 4) (px 0)
+ ".task-id" # hover ? textDecoration underline
+ ".priority" ? do
+ fontSize (px 12)
+ color "#6b7280"
+ ".task-title" ? do
+ fontSize (px 16)
+ margin (px 0) (px 0) (px 0) (px 0)
+ ".empty-msg" ? do
+ color "#6b7280"
+ fontStyle italic
+ ".ready-link" ? do
+ fontSize (px 14)
+ color "#0066cc"
+ ".count-badge" ? do
+ backgroundColor "#0066cc"
+ color white
+ padding (px 4) (px 10) (px 4) (px 10)
+ borderRadius (px 12) (px 12) (px 12) (px 12)
+ fontSize (px 14)
+ verticalAlign middle
+ ".description" ? do
+ backgroundColor "#f9fafb"
+ padding (px 12) (px 12) (px 12) (px 12)
+ borderRadius (px 4) (px 4) (px 4) (px 4)
+ margin (px 0) (px 0) (px 0) (px 0)
+ ".diff-block" ? do
+ maxHeight (px 600)
+ overflowY auto
+ ".no-commit-msg" ? do
+ backgroundColor "#fff3cd"
+ border (px 1) solid "#ffc107"
+ borderRadius (px 8) (px 8) (px 8) (px 8)
+ padding (px 16) (px 16) (px 16) (px 16)
+ margin (px 16) (px 0) (px 16) (px 0)
+ ".conflict-warning" ? do
+ backgroundColor "#fee2e2"
+ border (px 1) solid "#ef4444"
+ borderRadius (px 8) (px 8) (px 8) (px 8)
+ padding (px 16) (px 16) (px 16) (px 16)
+ margin (px 16) (px 0) (px 16) (px 0)
+
+statusBadges :: Css
+statusBadges = do
+ ".badge" ? do
+ display inlineBlock
+ padding (px 4) (px 10) (px 4) (px 10)
+ borderRadius (px 20) (px 20) (px 20) (px 20)
+ fontSize (px 12)
+ fontWeight (weight 500)
+ whiteSpace nowrap
+ ".badge-open" ? do
+ backgroundColor "#fef3c7"
+ color "#92400e"
+ ".badge-inprogress" ? do
+ backgroundColor "#dbeafe"
+ color "#1e40af"
+ ".badge-review" ? do
+ backgroundColor "#ede9fe"
+ color "#6b21a8"
+ ".badge-approved" ? do
+ backgroundColor "#cffafe"
+ color "#0e7490"
+ ".badge-done" ? do
+ backgroundColor "#d1fae5"
+ color "#065f46"
+
+buttonStyles :: Css
+buttonStyles = do
+ ".btn"
+ <> ".action-btn"
+ <> ".filter-btn"
+ <> ".submit-btn"
+ <> ".accept-btn"
+ <> ".reject-btn"
+ <> ".review-link-btn"
+ ? do
+ display inlineBlock
+ minHeight (px 44)
+ padding (px 10) (px 20) (px 10) (px 20)
+ borderRadius (px 6) (px 6) (px 6) (px 6)
+ border (px 0) none transparent
+ fontSize (px 14)
+ fontWeight (weight 500)
+ textDecoration none
+ cursor pointer
+ textAlign center
+ transition "all" (ms 150) ease (sec 0)
+ Stylesheet.key "touch-action" ("manipulation" :: Text)
+ ".action-btn" ? do
+ backgroundColor white
+ border (px 1) solid "#d1d5db"
+ color "#374151"
+ ".action-btn" # hover ? do
+ backgroundColor "#f9fafb"
+ borderColor "#9ca3af"
+ ".action-btn-primary" <> ".filter-btn" <> ".submit-btn" ? do
+ backgroundColor "#0066cc"
+ color white
+ borderColor "#0066cc"
+ ".action-btn-primary"
+ # hover
+ <> ".filter-btn"
+ # hover
+ <> ".submit-btn"
+ # hover
+ ? do
+ backgroundColor "#0052a3"
+ ".accept-btn" ? do
+ backgroundColor "#10b981"
+ color white
+ ".accept-btn" # hover ? backgroundColor "#059669"
+ ".reject-btn" ? do
+ backgroundColor "#ef4444"
+ color white
+ ".reject-btn" # hover ? backgroundColor "#dc2626"
+ ".clear-btn" ? do
+ display inlineBlock
+ minHeight (px 44)
+ padding (px 10) (px 16) (px 10) (px 16)
+ backgroundColor "#6b7280"
+ color white
+ borderRadius (px 6) (px 6) (px 6) (px 6)
+ textDecoration none
+ fontSize (px 14)
+ cursor pointer
+ ".clear-btn" # hover ? backgroundColor "#4b5563"
+ ".review-link-btn" ? do
+ backgroundColor "#8b5cf6"
+ color white
+ ".review-link-btn" # hover ? backgroundColor "#7c3aed"
+ ".review-link-section" ? margin (px 16) (px 0) (px 16) (px 0)
+
+formStyles :: Css
+formStyles = do
+ ".filter-row" ? do
+ display flex
+ flexWrap Flexbox.wrap
+ Stylesheet.key "gap" ("12px" :: Text)
+ alignItems flexEnd
+ ".filter-group" ? do
+ display flex
+ flexDirection column
+ Stylesheet.key "gap" ("4px" :: Text)
+ (".filter-group" |> label) ? do
+ fontSize (px 12)
+ color "#6b7280"
+ fontWeight (weight 500)
+ ".filter-select" <> ".filter-input" <> ".status-select" ? do
+ minHeight (px 44)
+ padding (px 10) (px 14) (px 10) (px 14)
+ border (px 1) solid "#d1d5db"
+ borderRadius (px 6) (px 6) (px 6) (px 6)
+ fontSize (px 14)
+ minWidth (px 120)
+ ".filter-input" ? minWidth (px 150)
+ ".inline-form" ? display inlineBlock
+ ".reject-form" ? do
+ display flex
+ Stylesheet.key "gap" ("8px" :: Text)
+ Stylesheet.key "flex" ("1" :: Text)
+ minWidth (px 250)
+ flexWrap Flexbox.wrap
+ ".reject-notes" ? do
+ Stylesheet.key "flex" ("1" :: Text)
+ minWidth (px 200)
+ minHeight (px 44)
+ padding (px 10) (px 14) (px 10) (px 14)
+ border (px 1) solid "#d1d5db"
+ borderRadius (px 6) (px 6) (px 6) (px 6)
+ fontSize (px 14)
+ Stylesheet.key "resize" ("vertical" :: Text)
+
+responsiveStyles :: Css
+responsiveStyles = do
+ query Media.screen [Media.maxWidth (px 600)] <| do
+ body ? fontSize (px 15)
+ ".container" ? padding (px 12) (px 12) (px 12) (px 12)
+ ".nav-content" ? do
+ flexDirection column
+ alignItems flexStart
+ ".stats-grid" ? do
+ Stylesheet.key "grid-template-columns" ("repeat(2, 1fr)" :: Text)
+ ".detail-row" ? do
+ flexDirection column
+ Stylesheet.key "gap" ("4px" :: Text)
+ ".detail-label" ? width auto
+ ".filter-row" ? flexDirection column
+ ".filter-group" ? width (pct 100)
+ ".filter-select" <> ".filter-input" ? width (pct 100)
+ ".review-actions" ? do
+ flexDirection column
+ ".reject-form" ? do
+ width (pct 100)
+ flexDirection column
+ ".reject-notes" ? width (pct 100)
+ ".actions" ? flexDirection column
+ ".action-btn" ? width (pct 100)
+
+darkModeStyles :: Css
+darkModeStyles =
+ query Media.screen [prefersDark] <| do
+ body ? do
+ backgroundColor "#111827"
+ color "#f3f4f6"
+ ".card"
+ <> ".task-card"
+ <> ".stat-card"
+ <> ".task-detail"
+ <> ".task-summary"
+ <> ".filter-form"
+ <> ".status-form"
+ <> ".diff-section"
+ <> ".review-actions"
+ ? do
+ backgroundColor "#1f2937"
+ boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.3) (shadow (px 0) (px 2))))
+ header ? do
+ backgroundColor "#1f2937"
+ boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.3) (shadow (px 0) (px 2))))
+ ".nav-brand" ? color "#f3f4f6"
+ "h2" <> "h3" ? color "#d1d5db"
+ a ? color "#60a5fa"
+ ".detail-row" ? borderBottomColor "#374151"
+ ".detail-label"
+ <> ".priority"
+ <> ".dep-type"
+ <> ".child-status"
+ <> ".empty-msg"
+ <> ".stat-label"
+ <> ".priority-desc"
+ ? color "#9ca3af"
+ ".child-title" ? color "#d1d5db"
+ code ? do
+ backgroundColor "#374151"
+ color "#f3f4f6"
+ ".detail-section" ? borderTopColor "#374151"
+ ".description" ? backgroundColor "#374151"
+ ".badge-open" ? do
+ backgroundColor "#78350f"
+ color "#fcd34d"
+ ".badge-inprogress" ? do
+ backgroundColor "#1e3a8a"
+ color "#93c5fd"
+ ".badge-review" ? do
+ backgroundColor "#4c1d95"
+ color "#c4b5fd"
+ ".badge-approved" ? do
+ backgroundColor "#164e63"
+ color "#67e8f9"
+ ".badge-done" ? do
+ backgroundColor "#064e3b"
+ color "#6ee7b7"
+ ".action-btn" ? do
+ backgroundColor "#374151"
+ borderColor "#4b5563"
+ color "#f3f4f6"
+ ".action-btn" # hover ? backgroundColor "#4b5563"
+ ".filter-select" <> ".filter-input" <> ".status-select" <> ".reject-notes" ? do
+ backgroundColor "#374151"
+ borderColor "#4b5563"
+ color "#f3f4f6"
+
+prefersDark :: Stylesheet.Feature
+prefersDark =
+ Stylesheet.Feature "prefers-color-scheme" (Just (Clay.value ("dark" :: Text)))
+
+statusBadgeClass :: Text -> Text
+statusBadgeClass status = case status of
+ "Open" -> "badge badge-open"
+ "InProgress" -> "badge badge-inprogress"
+ "Review" -> "badge badge-review"
+ "Approved" -> "badge badge-approved"
+ "Done" -> "badge badge-done"
+ _ -> "badge"
+
+priorityBadgeClass :: Text -> Text
+priorityBadgeClass priority = case priority of
+ "P0" -> "badge badge-p0"
+ "P1" -> "badge badge-p1"
+ "P2" -> "badge badge-p2"
+ "P3" -> "badge badge-p3"
+ "P4" -> "badge badge-p4"
+ _ -> "badge"