nano-ui-0.1.0.0: lib/NanoUI/Style.hs
{-# LANGUAGE StrictData #-}
module NanoUI.Style
( Sizing (..)
, Direction (..)
, AlignX (..)
, AlignY (..)
, Padding (..)
, Layout (..)
, defaultLayout
, Style (..)
, Theme (..)
, defaultTheme
, tomorrowNightMinDarkTheme
, tomorrowMinLightTheme
, tomorrowMidnightMinDarkTheme
, Base16 (..)
, themeFromBase16
, themeFromBase16Dark
, themeFromBase16Light
, base16TomorrowNight
, base16TomorrowLight
-- * Style modifiers
, background
, foreground
, borderColor
, borderWidth
, cornerRadius
, hoverBackground
, pressBackground
, fillColor
-- * Theme modifiers
, buttonStyle
, inputStyle
, panelStyle
, windowStyle
, everyStyle
, accentColor
, textColor
, mutedColor
, linkColor
, selectionColor
, windowColor
, rounded
, tinted
, primary
, destructive
, success
, subtle
, readableOn
, disabledTheme
, themeSeries
, separatorTrackColor
, scrollBarTrackColor
, scrollBarThumbColor
, fadeAlpha
, windowPad
, windowMargin
, padAll
, padXY
, gap
, fillW
, fillH
, grow
, minW
, maxW
, fixedW
, minH
, maxH
, fixedH
, fixedWH
, alignMid
, alignEnd
, tight
, percent
, gridMinColW
, fixedAspectW
, fixedAspectH
, gridCols
, FontVariant (..)
, FontWeight (..)
, FontStyle (..)
, TextDecoration (..)
, LayoutModifier
, fontRegular
, fontHeading
, fontMuted
, fontMono
, fontDanger
, fontSize
, fontSizeScale
, fontColor
, fontWeight
, fontBold
, fontLight
, fontMedium
, fontSemiBold
, fontExtraBold
, fontBlack
, fontStyle
, fontItalic
, fontOblique
, textDecoration
, fontUnderline
, fontStrike
, alignStart
, alignCenter
, alignTop
, alignBottom
) where
import Data.Bits ((.&.), (.|.))
import Data.Word (Word8)
import NanoUI.Types (Color (..), colorA, colorLuminance, colorRGBA, contrastRatio, lerpColor)
data Sizing
= Fixed Float
| Fit
| Grow Float
| Shrink Float
| Percent Float
deriving (Eq, Show)
data Direction = Row | Column
deriving (Eq, Show, Enum, Bounded)
data AlignX = AlignStart | AlignCenter | AlignEnd
deriving (Eq, Show, Enum, Bounded)
data AlignY = AlignTop | AlignMiddle | AlignBottom
deriving (Eq, Show, Enum, Bounded)
data Padding = Padding
{ padL :: {-# UNPACK #-} !Float
, padR :: {-# UNPACK #-} !Float
, padT :: {-# UNPACK #-} !Float
, padB :: {-# UNPACK #-} !Float
}
deriving (Eq, Show)
-- Floating window chrome. The body sits one side-pad below the chrome and one
-- side-pad above the window's bottom edge (the window's own column gap fills
-- the top; see 'NanoUI.Widgets.Overlay').
windowPad :: Padding
windowPad = Padding 10 10 0 10
-- Screen inset for floating window/modal max size and default placement.
windowMargin :: Float
windowMargin = 14
data FontVariant
= FontRegular
| FontHeading
| FontMuted
| FontMono
| FontDanger
deriving (Eq, Show, Enum, Bounded, Ord)
data FontWeight
= WeightNormal
| WeightBold
| WeightLight
| WeightMedium
| WeightSemiBold
| WeightExtraBold
| WeightBlack
deriving (Eq, Show, Enum, Bounded, Ord)
data FontStyle
= FontStyleNormal
| FontStyleItalic
| FontStyleOblique
deriving (Eq, Show, Enum, Bounded, Ord)
data TextDecoration
= DecorationNone
| DecorationUnderline
| DecorationStrikethrough
| DecorationUnderlineStrike
deriving (Eq, Show, Enum, Bounded, Ord)
type LayoutModifier = Layout -> Layout
data Layout = Layout
{ layoutDirection :: !Direction
, layoutWidth :: !Sizing
, layoutHeight :: !Sizing
, layoutPadding :: !Padding
, layoutGap :: {-# UNPACK #-} !Float
, layoutAlignX :: !AlignX
, layoutAlignY :: !AlignY
, layoutMinW :: {-# UNPACK #-} !Float
, layoutMinH :: {-# UNPACK #-} !Float
, layoutMaxW :: {-# UNPACK #-} !Float
, layoutMaxH :: {-# UNPACK #-} !Float
, layoutFontVariant :: !FontVariant
, layoutGridCols :: {-# UNPACK #-} !Int
, layoutGridMinColW :: {-# UNPACK #-} !Float
, layoutFontSize :: {-# UNPACK #-} !Float
, layoutFontColor :: !(Maybe Color)
, layoutFontWeight :: !FontWeight
, layoutFontStyle :: !FontStyle
, layoutTextDecoration :: !TextDecoration
}
deriving (Eq, Show)
defaultLayout :: Layout
defaultLayout =
Layout
{ layoutDirection = Column
, layoutWidth = Fit
, layoutHeight = Fit
, layoutPadding = Padding 3 3 3 3
, layoutGap = 8
, layoutAlignX = AlignStart
, layoutAlignY = AlignTop
, layoutMinW = 0
, layoutMinH = 0
, layoutMaxW = 1e9
, layoutMaxH = 1e9
, layoutFontVariant = FontRegular
, layoutGridCols = 0
, layoutGridMinColW = 0
, layoutFontSize = 0
, layoutFontColor = Nothing
, layoutFontWeight = WeightNormal
, layoutFontStyle = FontStyleNormal
, layoutTextDecoration = DecorationNone
}
padAll :: Float -> Layout -> Layout
padAll n l = l {layoutPadding = Padding n n n n}
padXY :: Float -> Float -> Layout -> Layout
padXY x y l = l {layoutPadding = Padding x x y y}
gap :: Float -> Layout -> Layout
gap n l = l {layoutGap = n}
fillW :: Layout -> Layout
fillW l = l {layoutWidth = Grow 1}
fillH :: Layout -> Layout
fillH l = l {layoutHeight = Grow 1}
grow :: Layout -> Layout
grow = fillW . fillH
minW :: Float -> Layout -> Layout
minW n l = l {layoutMinW = n}
maxW :: Float -> Layout -> Layout
maxW n l = l {layoutMaxW = n}
fixedW :: Float -> Layout -> Layout
fixedW n l = l {layoutWidth = Fixed n, layoutMinW = n, layoutMaxW = n}
minH :: Float -> Layout -> Layout
minH n l = l {layoutMinH = n}
maxH :: Float -> Layout -> Layout
maxH n l = l {layoutMaxH = n}
fixedH :: Float -> Layout -> Layout
fixedH n l = l {layoutHeight = Fixed n}
fixedWH :: Float -> Float -> Layout -> Layout
fixedWH w h l = l {layoutWidth = Fixed w, layoutHeight = Fixed h}
alignMid :: Layout -> Layout
alignMid l = l {layoutAlignY = AlignMiddle}
alignEnd :: Layout -> Layout
alignEnd l = l {layoutAlignX = AlignEnd}
tight :: Layout -> Layout
tight l = l {layoutPadding = Padding 0 0 0 0}
percent :: Float -> Layout -> Layout
percent p l = l {layoutWidth = Percent p}
gridMinColW :: Float -> Layout -> Layout
gridMinColW w l = l {layoutGridMinColW = max 0 w}
fixedAspectW :: Float -> Float -> Layout -> Layout
fixedAspectW w ratio = fixedWH w (w / ratio)
fixedAspectH :: Float -> Float -> Layout -> Layout
fixedAspectH h ratio = fixedWH (h * ratio) h
gridCols :: Int -> Layout -> Layout
gridCols n l = l {layoutGridCols = max 0 n}
fontRegular :: Layout -> Layout
fontRegular l = l {layoutFontVariant = FontRegular}
fontHeading :: Layout -> Layout
fontHeading l = l {layoutFontVariant = FontHeading}
fontMuted :: Layout -> Layout
fontMuted l = l {layoutFontVariant = FontMuted}
fontMono :: Layout -> Layout
fontMono l = l {layoutFontVariant = FontMono}
fontDanger :: Layout -> Layout
fontDanger l = l {layoutFontVariant = FontDanger}
fontSize :: Float -> Layout -> Layout
fontSize sz l = l {layoutFontSize = max 0 sz}
fontSizeScale :: Float -> Layout -> Layout
fontSizeScale s l =
let cur = layoutFontSize l
sz = if cur > 0 then cur * s else 16 * s
in l {layoutFontSize = max 0 sz}
fontColor :: Color -> Layout -> Layout
fontColor col l = l {layoutFontColor = Just col}
fontWeight :: FontWeight -> Layout -> Layout
fontWeight w l = l {layoutFontWeight = w}
fontBold :: Layout -> Layout
fontBold = fontWeight WeightBold
fontLight :: Layout -> Layout
fontLight = fontWeight WeightLight
fontMedium :: Layout -> Layout
fontMedium = fontWeight WeightMedium
fontSemiBold :: Layout -> Layout
fontSemiBold = fontWeight WeightSemiBold
fontExtraBold :: Layout -> Layout
fontExtraBold = fontWeight WeightExtraBold
fontBlack :: Layout -> Layout
fontBlack = fontWeight WeightBlack
fontStyle :: FontStyle -> Layout -> Layout
fontStyle s l = l {layoutFontStyle = s}
fontItalic :: Layout -> Layout
fontItalic = fontStyle FontStyleItalic
fontOblique :: Layout -> Layout
fontOblique = fontStyle FontStyleOblique
textDecoration :: TextDecoration -> Layout -> Layout
textDecoration d l = l {layoutTextDecoration = d}
fontUnderline :: Layout -> Layout
fontUnderline l =
let newDeco = case layoutTextDecoration l of
DecorationStrikethrough -> DecorationUnderlineStrike
DecorationUnderlineStrike -> DecorationUnderlineStrike
_ -> DecorationUnderline
in l {layoutTextDecoration = newDeco}
fontStrike :: Layout -> Layout
fontStrike l =
let newDeco = case layoutTextDecoration l of
DecorationUnderline -> DecorationUnderlineStrike
DecorationUnderlineStrike -> DecorationUnderlineStrike
_ -> DecorationStrikethrough
in l {layoutTextDecoration = newDeco}
alignStart :: Layout -> Layout
alignStart l = l {layoutAlignX = AlignStart}
alignCenter :: Layout -> Layout
alignCenter l = l {layoutAlignX = AlignCenter}
alignTop :: Layout -> Layout
alignTop l = l {layoutAlignY = AlignTop}
alignBottom :: Layout -> Layout
alignBottom l = l {layoutAlignY = AlignBottom}
data Style = Style
{ styleBg :: {-# UNPACK #-} !Color
, styleFg :: {-# UNPACK #-} !Color
, styleBorder :: {-# UNPACK #-} !Color
, styleBorderWidth :: {-# UNPACK #-} !Float
, styleCornerRadius :: {-# UNPACK #-} !Float
, styleHoverBg :: {-# UNPACK #-} !Color
, styleActiveBg :: {-# UNPACK #-} !Color
}
deriving (Eq, Show)
data Theme = Theme
{ themeWindow :: {-# UNPACK #-} !Color
, themePanel :: !Style
, themeFloatingWindow :: !Style
, themeButton :: !Style
, themeInput :: !Style
, themeSeparator :: {-# UNPACK #-} !Color
, themeAccent :: {-# UNPACK #-} !Color
, themeMuted :: {-# UNPACK #-} !Color
, themeRed :: {-# UNPACK #-} !Color
, themeOrange :: {-# UNPACK #-} !Color
, themeYellow :: {-# UNPACK #-} !Color
, themeGreen :: {-# UNPACK #-} !Color
, themePurple :: {-# UNPACK #-} !Color
, themeOverlayDim :: {-# UNPACK #-} !Color
, themeOnAccent :: {-# UNPACK #-} !Color
-- ^ Text and marks drawn on an accent fill: a checked box, an active tab,
-- a primary button.
, themeSelection :: {-# UNPACK #-} !Color
-- ^ Selected text's highlight, drawn under the text. Usually translucent.
, themeFocusRing :: {-# UNPACK #-} !Color
, themeLink :: {-# UNPACK #-} !Color
, themeShadow :: {-# UNPACK #-} !Color
-- ^ Offset shadow under menus, dropdowns and floating windows. A zero alpha
-- draws none.
, themeDisabledFade :: {-# UNPACK #-} !Float
-- ^ How far a disabled widget's colours fade toward the window colour, from
-- 0 (not at all) to 1 (invisible).
}
deriving (Eq, Show)
-- -----------------------------------------------------------------------------
-- Style and theme modifiers
-- -----------------------------------------------------------------------------
-- $modifiers
-- Styles and themes change the way layouts do: through functions that
-- compose with @(.)@. A @Style -> Style@ edits one surface, and a
-- @Theme -> Theme@ edits the theme a part of the view is drawn with (see
-- @styled@ in "NanoUI"):
--
-- > styled (buttonStyle (cornerRadius 8) . accentColor teal) $ do ...
-- > styled primary (button "Save")
background :: Color -> Style -> Style
background c s = s {styleBg = c}
foreground :: Color -> Style -> Style
foreground c s = s {styleFg = c}
borderColor :: Color -> Style -> Style
borderColor c s = s {styleBorder = c}
borderWidth :: Float -> Style -> Style
borderWidth w s = s {styleBorderWidth = max 0 w}
cornerRadius :: Float -> Style -> Style
cornerRadius r s = s {styleCornerRadius = max 0 r}
hoverBackground :: Color -> Style -> Style
hoverBackground c s = s {styleHoverBg = c}
pressBackground :: Color -> Style -> Style
pressBackground c s = s {styleActiveBg = c}
-- | A background with hover and press shades derived from it: hovering mixes
-- in some of the foreground, pressing darkens.
fillColor :: Color -> Style -> Style
fillColor c s =
s
{ styleBg = c
, styleHoverBg = lerpColor c (styleFg s) 0.12
, styleActiveBg = lerpColor c (colorRGBA 0 0 0 (colorA c)) 0.18
}
buttonStyle :: (Style -> Style) -> Theme -> Theme
buttonStyle f t = t {themeButton = f (themeButton t)}
-- | Text fields, text areas, sliders' wells and scroller wells.
inputStyle :: (Style -> Style) -> Theme -> Theme
inputStyle f t = t {themeInput = f (themeInput t)}
-- | Panels, cards, menus, and label text.
panelStyle :: (Style -> Style) -> Theme -> Theme
panelStyle f t = t {themePanel = f (themePanel t)}
-- | Floating windows.
windowStyle :: (Style -> Style) -> Theme -> Theme
windowStyle f t = t {themeFloatingWindow = f (themeFloatingWindow t)}
everyStyle :: (Style -> Style) -> Theme -> Theme
everyStyle f = buttonStyle f . inputStyle f . panelStyle f . windowStyle f
accentColor :: Color -> Theme -> Theme
accentColor c t = t {themeAccent = c, themeFocusRing = c, themeSelection = fadeAlpha c (colorA (themeSelection t))}
-- | The foreground of every surface.
textColor :: Color -> Theme -> Theme
textColor c = everyStyle (foreground c)
mutedColor :: Color -> Theme -> Theme
mutedColor c t = t {themeMuted = c}
linkColor :: Color -> Theme -> Theme
linkColor c t = t {themeLink = c}
selectionColor :: Color -> Theme -> Theme
selectionColor c t = t {themeSelection = c}
-- | The backdrop behind everything, which disabled widgets also fade toward.
windowColor :: Color -> Theme -> Theme
windowColor c t = t {themeWindow = c}
-- | The corner radius of every surface.
rounded :: Float -> Theme -> Theme
rounded r = everyStyle (cornerRadius r)
-- | Buttons filled with a colour picked from the theme, with a readable label.
--
-- > styled (tinted themePurple) (button "Tag")
tinted :: (Theme -> Color) -> Theme -> Theme
tinted pick t =
let c = pick t
label = readableOn t c
in buttonStyle
( \s ->
s
{ styleBg = c
, styleFg = label
, styleBorder = c
, styleHoverBg = lerpColor c label 0.14
, styleActiveBg = lerpColor c (themeWindow t) 0.22
}
)
t
-- | Buttons in the accent colour, for the action a view is for.
primary :: Theme -> Theme
primary = tinted themeAccent
-- | Buttons in the theme's red, for destructive actions.
destructive :: Theme -> Theme
destructive = tinted themeRed
success :: Theme -> Theme
success = tinted themeGreen
-- | Buttons without a fill or border until hovered, for toolbars and
-- secondary actions.
subtle :: Theme -> Theme
subtle =
buttonStyle $ \s ->
s
{ styleBg = clear
, styleBorder = clear
, styleBorderWidth = 0
, styleHoverBg = fadeAlpha (styleFg s) 30
, styleActiveBg = fadeAlpha (styleFg s) 48
}
where
clear = colorRGBA 0 0 0 0
-- | Whichever of the theme's text colours reads best on @c@.
readableOn :: Theme -> Color -> Color
readableOn t c =
let candidates = [themeOnAccent t, styleFg (themePanel t), themeWindow t]
best a b = if contrastRatio a c >= contrastRatio b c then a else b
in foldr1 best candidates
-- | The theme disabled widgets are drawn with: every colour faded toward the
-- window colour by 'themeDisabledFade', and no hover or press feedback.
disabledTheme :: Theme -> Theme
disabledTheme t =
let f = themeDisabledFade t
fade c
| colorA c == 0 = c
| otherwise = fadeAlpha (lerpColor c (themeWindow t) f) (colorA c)
fadeStyle s =
let bg = fade (styleBg s)
in s {styleBg = bg, styleFg = fade (styleFg s), styleBorder = fade (styleBorder s), styleHoverBg = bg, styleActiveBg = bg}
in t
{ themePanel = fadeStyle (themePanel t)
, themeFloatingWindow = fadeStyle (themeFloatingWindow t)
, themeButton = fadeStyle (themeButton t)
, themeInput = fadeStyle (themeInput t)
, themeSeparator = fade (themeSeparator t)
, themeAccent = fade (themeAccent t)
, themeMuted = fade (themeMuted t)
, themeRed = fade (themeRed t)
, themeOrange = fade (themeOrange t)
, themeYellow = fade (themeYellow t)
, themeGreen = fade (themeGreen t)
, themePurple = fade (themePurple t)
, themeOnAccent = fade (themeOnAccent t)
, themeFocusRing = fade (themeFocusRing t)
, themeLink = fade (themeLink t)
}
-- | Flat widget style: bg/fg/border plus hover and active fills.
-- Border width 1 and corner radius 2, as the built-in themes use.
flatStyle :: Color -> Color -> Color -> Color -> Color -> Style
flatStyle bg fg border hoverBg activeBg =
Style
{ styleBg = bg
, styleFg = fg
, styleBorder = border
, styleBorderWidth = 1
, styleCornerRadius = 2
, styleHoverBg = hoverBg
, styleActiveBg = activeBg
}
-- | Neutral charcoal surfaces, warm text, and a blue selection accent.
-- Keep structural edges quiet; interactive borders and focus carry contrast.
defaultTheme :: Theme
defaultTheme =
let panelSurface =
flatStyle
(colorRGBA 34 34 38 255)
(colorRGBA 236 234 230 255)
(colorRGBA 54 54 62 255)
(colorRGBA 34 34 38 255)
(colorRGBA 30 30 34 255)
in Theme
{ themeWindow = colorRGBA 24 24 27 255
, themePanel = panelSurface
, themeFloatingWindow = panelSurface
, themeButton =
flatStyle
(colorRGBA 52 52 58 255)
(colorRGBA 248 247 245 255)
(colorRGBA 74 76 84 255)
(colorRGBA 68 70 78 255)
(colorRGBA 42 42 48 255)
, themeInput =
flatStyle
(colorRGBA 18 18 21 255)
(colorRGBA 236 234 230 255)
(colorRGBA 70 72 80 255)
(colorRGBA 24 24 28 255)
(colorRGBA 14 14 17 255)
, themeSeparator = colorRGBA 62 64 72 255
, themeAccent = colorRGBA 88 156 246 255
, themeMuted = colorRGBA 176 172 164 255
, themeRed = colorRGBA 252 165 165 255
, themeOrange = colorRGBA 216 140 72 255
, themeYellow = colorRGBA 212 176 88 255
, themeGreen = colorRGBA 104 168 124 255
, themePurple = colorRGBA 176 140 220 255
, themeOverlayDim = colorRGBA 8 8 10 176
, themeOnAccent = colorRGBA 255 255 255 255
, themeSelection = fadeAlpha (colorRGBA 88 156 246 255) 115
, themeFocusRing = colorRGBA 88 156 246 255
, themeLink = colorRGBA 124 178 250 255
, themeShadow = colorRGBA 0 0 0 72
, themeDisabledFade = 0.55
}
-- Status and series colours in hue order, then accent.
themeSeries :: Theme -> [Color]
themeSeries t =
[ themeRed t
, themeOrange t
, themeYellow t
, themeGreen t
, themeAccent t
, themePurple t
]
-- | Opaque tint of a base surface toward the separator color: the track color
-- for scrollbar tracks, divider strips, and similar hairline chrome.
separatorTrackColor :: Style -> Theme -> Color
separatorTrackColor base theme =
lerpColor (styleBg base) (themeSeparator theme) 0.28
-- Scroll track/thumb tints. The track is an opaque mix of the surface it sits
-- on (the scroller well / floating window body) toward the separator colour, so
-- the lane reads against that surface on every theme. The thumb stays a
-- translucent foreground mix so the track shows through it.
scrollBarTrackColor :: Style -> Theme -> Color
scrollBarTrackColor base theme =
separatorTrackColor base theme
scrollBarThumbColor :: Style -> Theme -> Color
scrollBarThumbColor base theme =
let solid = lerpColor (themeSeparator theme) (styleFg base) 0.58
in fadeAlpha solid 130
-- | Replaces the alpha channel of a color.
fadeAlpha :: Color -> Word8 -> Color
fadeAlpha (Color w) a = Color ((w .&. 0xFFFFFF00) .|. fromIntegral a)
-- | Ported from "Tomorrow Night Min" in https://github.com/biaqat/tomorrow-min-theme-zed
tomorrowNightMinDarkTheme :: Theme
tomorrowNightMinDarkTheme =
let panelSurface =
flatStyle
(colorRGBA 30 31 33 255) -- base.bg #1E1F21 (elevated panel canvas)
(colorRGBA 234 234 234 255) -- bright.fg #EAEAEA
edgeCol
(colorRGBA 52 54 62 255) -- #34363E
(colorRGBA 26 27 29 255) -- #1A1B1D
in Theme
{ themeWindow = colorRGBA 23 24 26 255 -- #17181A (dark root window backdrop)
, themePanel = panelSurface
, themeFloatingWindow = panelSurface
, themeButton =
flatStyle
(colorRGBA 44 46 51 255) -- elevated button surface
(colorRGBA 245 245 245 255) -- bright.fg / white
edgeCol
(colorRGBA 69 74 83 255) -- #454A53
(colorRGBA 28 29 32 255) -- depressed on click
, themeInput =
flatStyle
(colorRGBA 23 24 26 255) -- #17181A (recessed into #1E1F21 panel)
(colorRGBA 234 234 234 255) -- bright.fg #EAEAEA
edgeCol
(colorRGBA 29 30 33 255)
(colorRGBA 19 20 22 255)
, themeSeparator = sepCol
, themeAccent = accentCol
, themeMuted = colorRGBA 150 152 150 255 -- comment #969896
, themeRed = colorRGBA 204 102 102 255 -- base.red #CC6666
, themeOrange = colorRGBA 222 147 95 255 -- base.orange #DE935F
, themeYellow = colorRGBA 240 198 116 255 -- base.yellow #F0C674
, themeGreen = colorRGBA 181 189 104 255 -- base.green #B5BD68
, themePurple = colorRGBA 178 148 187 255 -- base.purple #B294BB
, themeOverlayDim = colorRGBA 0 0 0 160
, themeOnAccent = colorRGBA 255 255 255 255
, themeSelection = fadeAlpha accentCol 115
, themeFocusRing = accentCol
, themeLink = accentCol
, themeShadow = colorRGBA 0 0 0 72
, themeDisabledFade = 0.55
}
where
edgeCol = colorRGBA 77 80 87 255 -- window #4D5057 (touch brighter crisp border)
sepCol = colorRGBA 55 59 65 255 -- base.selection #373B41 (subtle divider)
accentCol = colorRGBA 103 150 230 255 -- vscode.cornflower_blue #6796E6
-- | Ported from "Tomorrow Min" in https://github.com/biaqat/tomorrow-min-theme-zed
tomorrowMinLightTheme :: Theme
tomorrowMinLightTheme =
let panelSurface =
flatStyle
(colorRGBA 242 242 242 255) -- #F2F2F2
(colorRGBA 55 59 65 255) -- #373B41
(colorRGBA 222 222 222 255) -- #DEDEDE
(colorRGBA 231 231 231 255) -- #E7E7E7 (darker than #F2F2F2 so hover reads)
(colorRGBA 219 219 219 255) -- #DBDBDB
in Theme
{ themeWindow = colorRGBA 255 255 255 255 -- #FFFFFF
, themePanel = panelSurface
, themeFloatingWindow = panelSurface
, themeButton =
flatStyle
(colorRGBA 232 232 232 255) -- #E8E8E8 (step down from panel for zebra rows)
(colorRGBA 55 59 65 255) -- #373B41
(colorRGBA 214 214 214 255) -- #D6D6D6
(colorRGBA 214 214 214 255) -- #D6D6D6
(colorRGBA 196 196 196 255) -- #C4C4C4
, themeInput =
flatStyle
(colorRGBA 255 255 255 255) -- #FFFFFF
(colorRGBA 55 59 65 255)
(colorRGBA 210 210 210 255)
(colorRGBA 243 243 243 255) -- #F3F3F3 (darker than white so hover reads)
(colorRGBA 255 255 255 255) -- focus keeps the normal white bg; accent border signals focus
, themeSeparator = colorRGBA 222 222 222 255 -- #DEDEDE
, themeAccent = colorRGBA 82 134 188 255 -- #5286BC (Tomorrow Blue)
, themeMuted = colorRGBA 140 140 140 255 -- #8C8C8C
, themeRed = colorRGBA 197 78 82 255 -- Tomorrow Red #C54E52
, themeOrange = colorRGBA 231 140 69 255 -- Tomorrow Orange #E78C45
, themeYellow = colorRGBA 231 197 71 255 -- Tomorrow Yellow #E7C547
, themeGreen = colorRGBA 113 140 0 255 -- Tomorrow Green #718C00
, themePurple = colorRGBA 137 91 144 255 -- Tomorrow Purple #895B90
, themeOverlayDim = colorRGBA 0 0 0 100
, themeOnAccent = colorRGBA 255 255 255 255
, themeSelection = fadeAlpha (colorRGBA 82 134 188 255) 80
, themeFocusRing = colorRGBA 82 134 188 255
, themeLink = colorRGBA 66 113 174 255
, themeShadow = colorRGBA 0 0 0 36
, themeDisabledFade = 0.55
}
-- | Ported from "Tomorrow at Midnight Min" in https://github.com/biaqat/tomorrow-min-theme-zed
tomorrowMidnightMinDarkTheme :: Theme
tomorrowMidnightMinDarkTheme =
let panelSurface =
flatStyle
(colorRGBA 16 17 20 255) -- #101114 (elevated panel canvas)
(colorRGBA 238 238 238 255) -- #EEEEEE
edgeCol
(colorRGBA 46 48 56 255) -- #2E3038
(colorRGBA 12 13 15 255) -- #0C0D0F
in Theme
{ themeWindow = colorRGBA 0 0 0 255 -- #000000 (pitch black root window backdrop)
, themePanel = panelSurface
, themeFloatingWindow = panelSurface
, themeButton =
flatStyle
(colorRGBA 26 27 34 255) -- #1A1B22
(colorRGBA 238 238 238 255) -- #EEEEEE
edgeCol
(colorRGBA 54 58 72 255) -- #363A48
(colorRGBA 56 60 81 255) -- #383C51
, themeInput =
flatStyle
(colorRGBA 13 14 18 255) -- #0D0E12 (recessed into panel)
(colorRGBA 238 238 238 255)
edgeCol
(colorRGBA 21 22 28 255)
(colorRGBA 8 9 11 255)
, themeSeparator = sepCol
, themeAccent = accentCol
, themeMuted = colorRGBA 128 132 150 255 -- #808496
, themeRed = colorRGBA 213 78 83 255 -- bright.red #D54E53
, themeOrange = colorRGBA 231 140 69 255 -- bright.orange #E78C45
, themeYellow = colorRGBA 231 197 71 255 -- bright.yellow #E7C547
, themeGreen = colorRGBA 185 202 74 255 -- bright.green #B9CA4A
, themePurple = colorRGBA 195 151 216 255 -- bright.purple #C397D8
, themeOverlayDim = colorRGBA 0 0 0 160
, themeOnAccent = colorRGBA 255 255 255 255
, themeSelection = fadeAlpha accentCol 115
, themeFocusRing = accentCol
, themeLink = accentCol
, themeShadow = colorRGBA 0 0 0 96
, themeDisabledFade = 0.55
}
where
edgeCol = colorRGBA 48 52 70 255 -- #303446
sepCol = colorRGBA 48 52 70 255 -- #303446
accentCol = colorRGBA 140 182 226 255 -- #8CB6E2
-- -----------------------------------------------------------------------------
-- Base16 Colorschemes
-- -----------------------------------------------------------------------------
-- | Standard Base16 palette containing 16 styling tones and syntax colours
-- following Chris Kempson's Base16 specification.
data Base16 = Base16
{ base00 :: {-# UNPACK #-} !Color -- ^ Default Background
, base01 :: {-# UNPACK #-} !Color -- ^ Lighter Background (status bars, line numbers, panel backgrounds)
, base02 :: {-# UNPACK #-} !Color -- ^ Selection Background (active elements, subtle highlights)
, base03 :: {-# UNPACK #-} !Color -- ^ Comments, Invisibles, Line Highlighting (muted text, borders)
, base04 :: {-# UNPACK #-} !Color -- ^ Dark Foreground (status bar foreground, secondary text)
, base05 :: {-# UNPACK #-} !Color -- ^ Default Foreground, Caret, Delimiters, Operators
, base06 :: {-# UNPACK #-} !Color -- ^ Light Foreground
, base07 :: {-# UNPACK #-} !Color -- ^ Light Background / Highest contrast foreground
, base08 :: {-# UNPACK #-} !Color -- ^ Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted (Red)
, base09 :: {-# UNPACK #-} !Color -- ^ Integers, Boolean, Constants, XML Attributes, Markup Link Url (Orange)
, base0A :: {-# UNPACK #-} !Color -- ^ Classes, Markup Bold, Search Text Background (Yellow)
, base0B :: {-# UNPACK #-} !Color -- ^ Strings, Inherited Class, Markup Code, Diff Inserted (Green)
, base0C :: {-# UNPACK #-} !Color -- ^ Support, Regular Expressions, Escape Characters, Markup Quotes (Cyan)
, base0D :: {-# UNPACK #-} !Color -- ^ Functions, Methods, Attribute IDs, Headings (Blue / Primary Accent)
, base0E :: {-# UNPACK #-} !Color -- ^ Keywords, Storage, Selector, Markup Italic, Diff Changed (Purple / Magenta)
, base0F :: {-# UNPACK #-} !Color -- ^ Deprecated, Opening/Closing Embedded Language Tags (Brown)
}
deriving (Eq, Show)
-- | Calculate a 'Theme' from a 'Base16' colorscheme, automatically selecting
-- dark or light styling based on background vs foreground luminance.
themeFromBase16 :: Base16 -> Theme
themeFromBase16 b
| isDark = themeFromBase16Dark b
| otherwise = themeFromBase16Light b
where
isDark = colorLuminance (base00 b) < colorLuminance (base05 b)
-- | Calculate a dark 'Theme' from a 'Base16' colorscheme.
themeFromBase16Dark :: Base16 -> Theme
themeFromBase16Dark b =
let edgeCol = lerpColor (base02 b) (base03 b) 0.35
panelBg = lerpColor (base01 b) (base02 b) 0.3
panelSurface =
flatStyle
panelBg
(base05 b)
edgeCol
(lerpColor panelBg (base02 b) 0.5)
(lerpColor panelBg (base00 b) 0.4)
in Theme
{ themeWindow = base00 b
, themePanel = panelSurface
, themeFloatingWindow = panelSurface
, themeButton =
flatStyle
(base02 b)
(base07 b)
edgeCol
(lerpColor (base02 b) (base03 b) 0.4)
(base01 b)
, themeInput =
flatStyle
(base00 b)
(base05 b)
edgeCol
(base01 b)
(base00 b)
, themeSeparator = edgeCol
, themeAccent = base0D b
, themeMuted = base03 b
, themeRed = base08 b
, themeOrange = base09 b
, themeYellow = base0A b
, themeGreen = base0B b
, themePurple = base0E b
, themeOverlayDim = colorRGBA 0 0 0 160
, themeOnAccent = if colorLuminance (base0D b) > 0.6 then base00 b else colorRGBA 255 255 255 255
, themeSelection = fadeAlpha (base0D b) 115
, themeFocusRing = base0D b
, themeLink = base0D b
, themeShadow = colorRGBA 0 0 0 72
, themeDisabledFade = 0.55
}
-- | Calculate a light 'Theme' from a 'Base16' colorscheme.
themeFromBase16Light :: Base16 -> Theme
themeFromBase16Light b =
let edgeCol = base02 b
panelBg = lerpColor (base00 b) (base01 b) 0.5
panelSurface =
flatStyle
panelBg
(base05 b)
edgeCol
(lerpColor panelBg (base00 b) 0.4)
(lerpColor panelBg (base02 b) 0.4)
in Theme
{ themeWindow = base00 b
, themePanel = panelSurface
, themeFloatingWindow = panelSurface
, themeButton =
flatStyle
(base01 b)
(base05 b)
edgeCol
(base02 b)
(lerpColor (base02 b) (base03 b) 0.35)
, themeInput =
flatStyle
(base00 b)
(base05 b)
edgeCol
(lerpColor (base00 b) (base01 b) 0.3)
(lerpColor (base00 b) (base01 b) 0.6)
, themeSeparator = edgeCol
, themeAccent = base0D b
, themeMuted = base03 b
, themeRed = base08 b
, themeOrange = base09 b
, themeYellow = base0A b
, themeGreen = base0B b
, themePurple = base0E b
, themeOverlayDim = colorRGBA 0 0 0 100
, themeOnAccent = if colorLuminance (base0D b) > 0.6 then base07 b else colorRGBA 255 255 255 255
, themeSelection = fadeAlpha (base0D b) 80
, themeFocusRing = base0D b
, themeLink = base0D b
, themeShadow = colorRGBA 0 0 0 36
, themeDisabledFade = 0.55
}
-- | Tomorrow Night Base16 reference palette.
base16TomorrowNight :: Base16
base16TomorrowNight =
Base16
{ base00 = colorRGBA 29 31 33 255 -- #1D1F21
, base01 = colorRGBA 40 42 46 255 -- #282A2E
, base02 = colorRGBA 55 59 65 255 -- #373B41
, base03 = colorRGBA 150 152 150 255 -- #969896
, base04 = colorRGBA 180 183 180 255 -- #B4B7B4
, base05 = colorRGBA 197 200 198 255 -- #C5C8C6
, base06 = colorRGBA 224 224 224 255 -- #E0E0E0
, base07 = colorRGBA 255 255 255 255 -- #FFFFFF
, base08 = colorRGBA 213 78 83 255 -- #D54E53
, base09 = colorRGBA 231 140 69 255 -- #E78C45
, base0A = colorRGBA 231 197 71 255 -- #E7C547
, base0B = colorRGBA 185 202 74 255 -- #B9CA4A
, base0C = colorRGBA 112 192 186 255 -- #70C0BA
, base0D = colorRGBA 103 150 230 255 -- #6796E6
, base0E = colorRGBA 195 151 216 255 -- #C397D8
, base0F = colorRGBA 163 104 90 255 -- #A3685A
}
-- | Tomorrow Light Base16 reference palette.
base16TomorrowLight :: Base16
base16TomorrowLight =
Base16
{ base00 = colorRGBA 255 255 255 255 -- #FFFFFF
, base01 = colorRGBA 242 242 242 255 -- #F2F2F2
, base02 = colorRGBA 222 222 222 255 -- #DEDEDE
, base03 = colorRGBA 140 140 140 255 -- #8C8C8C
, base04 = colorRGBA 150 152 150 255 -- #969896
, base05 = colorRGBA 55 59 65 255 -- #373B41
, base06 = colorRGBA 40 42 46 255 -- #282A2E
, base07 = colorRGBA 29 31 33 255 -- #1D1F21
, base08 = colorRGBA 197 78 82 255 -- #C54E52
, base09 = colorRGBA 231 140 69 255 -- #E78C45
, base0A = colorRGBA 231 197 71 255 -- #E7C547
, base0B = colorRGBA 113 140 0 255 -- #718C00
, base0C = colorRGBA 62 153 159 255 -- #3E999F
, base0D = colorRGBA 82 134 188 255 -- #5286BC
, base0E = colorRGBA 137 91 144 255 -- #895B90
, base0F = colorRGBA 163 104 90 255 -- #A3685A
}