{-# 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 activityTimelineStyles 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 ".navbar" ? do backgroundColor white padding (px 12) (px 20) (px 12) (px 20) boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.08) (shadow (px 0) (px 2)))) marginBottom (px 16) display flex alignItems center justifyContent spaceBetween flexWrap Flexbox.wrap Stylesheet.key "gap" ("12px" :: Text) ".navbar-brand" ? do fontSize (px 22) fontWeight bold color "#0066cc" textDecoration none ".navbar-brand" # hover ? textDecoration none ".navbar-links" ? do display flex Stylesheet.key "gap" ("4px" :: Text) flexWrap Flexbox.wrap ".navbar-link" ? do display inlineBlock padding (px 8) (px 14) (px 8) (px 14) color "#374151" textDecoration none borderRadius (px 6) (px 6) (px 6) (px 6) fontSize (px 14) fontWeight (weight 500) transition "background-color" (ms 150) ease (sec 0) ".navbar-link" # hover ? do backgroundColor "#f3f4f6" textDecoration none 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 ".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-card-link" ? do display block textDecoration none color inherit cursor pointer ".task-card-link" # hover ? textDecoration none ".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) color "#374151" ".diff-block" ? do maxHeight (px 600) overflowY auto ".progress-bar" ? do height (px 8) backgroundColor "#e5e7eb" borderRadius (px 4) (px 4) (px 4) (px 4) overflow hidden marginTop (px 8) ".progress-fill" ? do height (pct 100) backgroundColor "#0066cc" borderRadius (px 4) (px 4) (px 4) (px 4) transition "width" (ms 300) ease (sec 0) ".stats-section" ? 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)))) ".stats-row" ? do display flex alignItems center Stylesheet.key "gap" ("12px" :: Text) padding (px 8) (px 0) (px 8) (px 0) borderBottom (px 1) solid "#e5e7eb" ".stats-row" # lastChild ? borderBottom (px 0) none transparent ".stats-label" ? do minWidth (px 100) fontWeight (weight 500) ".stats-bar-container" ? do Stylesheet.key "flex" ("1" :: Text) ".stats-count" ? do minWidth (px 40) textAlign (alignSide sideRight) fontWeight (weight 500) ".summary-section" ? 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)))) ".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) activityTimelineStyles :: Css activityTimelineStyles = do ".activity-section" ? do marginTop (em 1.5) 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)))) ".activity-timeline" ? do position relative paddingLeft (px 24) marginTop (px 12) ".activity-timeline" # before ? do Stylesheet.key "content" ("''" :: Text) position absolute left (px 8) top (px 0) bottom (px 0) width (px 2) backgroundColor "#e5e7eb" ".activity-item" ? do position relative display flex Stylesheet.key "gap" ("12px" :: Text) paddingBottom (px 16) marginBottom (px 0) ".activity-item" # lastChild ? paddingBottom (px 0) ".activity-icon" ? do position absolute left (px (-20)) width (px 18) height (px 18) borderRadius (pct 50) (pct 50) (pct 50) (pct 50) display flex alignItems center justifyContent center fontSize (px 10) fontWeight bold backgroundColor white border (px 2) solid "#e5e7eb" ".activity-content" ? do Stylesheet.key "flex" ("1" :: Text) ".activity-header" ? do display flex alignItems center Stylesheet.key "gap" ("8px" :: Text) marginBottom (px 4) ".activity-stage" ? do fontWeight (weight 600) fontSize (px 14) ".activity-time" ? do fontSize (px 12) color "#6b7280" ".activity-message" ? do margin (px 4) (px 0) (px 0) (px 0) fontSize (px 14) color "#374151" ".activity-metadata" ? do marginTop (px 8) (".activity-metadata" |> "summary") ? do fontSize (px 12) color "#6b7280" cursor pointer ".metadata-json" ? do fontSize (px 11) backgroundColor "#f3f4f6" padding (px 8) (px 8) (px 8) (px 8) borderRadius (px 4) (px 4) (px 4) (px 4) marginTop (px 4) maxHeight (px 200) overflow auto ".stage-claiming" |> ".activity-icon" ? do borderColor "#3b82f6" color "#3b82f6" ".stage-running" |> ".activity-icon" ? do borderColor "#f59e0b" color "#f59e0b" ".stage-reviewing" |> ".activity-icon" ? do borderColor "#8b5cf6" color "#8b5cf6" ".stage-retrying" |> ".activity-icon" ? do borderColor "#f97316" color "#f97316" ".stage-completed" |> ".activity-icon" ? do borderColor "#10b981" color "#10b981" ".stage-failed" |> ".activity-icon" ? do borderColor "#ef4444" color "#ef4444" 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) ".navbar" ? do flexDirection column alignItems flexStart padding (px 12) (px 12) (px 12) (px 12) ".navbar-links" ? do width (pct 100) justifyContent center ".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)))) ".navbar" ? do backgroundColor "#1f2937" boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.3) (shadow (px 0) (px 2)))) ".navbar-brand" ? color "#60a5fa" ".navbar-link" ? color "#d1d5db" ".navbar-link" # hover ? backgroundColor "#374151" ".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" ".stats-section" <> ".summary-section" ? do backgroundColor "#1f2937" boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.3) (shadow (px 0) (px 2)))) ".stats-row" ? borderBottomColor "#374151" ".progress-bar" ? backgroundColor "#374151" ".progress-fill" ? backgroundColor "#60a5fa" ".activity-section" ? do backgroundColor "#1f2937" boxShadow (NE.singleton (bsColor (rgba 0 0 0 0.3) (shadow (px 0) (px 2)))) ".activity-timeline" # before ? backgroundColor "#374151" ".activity-icon" ? do backgroundColor "#1f2937" borderColor "#374151" ".activity-time" ? color "#9ca3af" ".activity-message" ? color "#d1d5db" (".activity-metadata" |> "summary") ? color "#9ca3af" ".metadata-json" ? backgroundColor "#374151" 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"