diff options
Diffstat (limited to 'Omni/Jr/Web')
| -rw-r--r-- | Omni/Jr/Web/Style.hs | 510 |
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" |
