All Downloads are FREE. Search and download functionalities are using the official Maven repository.

laika.helium.generate.CSSVarGenerator.scala Maven / Gradle / Ivy

/*
 * Copyright 2012-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package laika.helium.generate

import laika.ast.RelativePath
import laika.ast.Path.Root
import laika.helium.config.{ColorSet, CommonSettings, DarkModeSupport, EPUBSettings, SiteSettings}
import laika.theme.config.FontDefinition

private[helium] object CSSVarGenerator {

  private val darkModeMediaQuery = "@media (prefers-color-scheme: dark) {"
  
  private def generateFontFace (fontDef: FontDefinition, path: RelativePath): String = 
    s"""@font-face {
        |  font-family: "${fontDef.family}";
        |  font-weight: ${fontDef.weight.value.toLowerCase};
        |  font-style: ${fontDef.style.value.toLowerCase};
        |  src: url("$path");
        |}""".stripMargin
  
  def generate (settings: SiteSettings): String = {
    import settings.layout._
    val layoutStyles = Seq(
      "content-width"  -> contentWidth.displayValue,
      "nav-width"      -> navigationWidth.displayValue,
      "top-bar-height" -> topBarHeight.displayValue
    )
    generate(settings, layoutStyles, settings.layout.topNavigationBar.highContrast)
  }
  
  def generate (settings: EPUBSettings): String = {
    val embeddedFonts = settings.bookConfig.fonts.flatMap { font =>
      font.resource.embedResource.map { res =>
        generateFontFace(font, res.path.relativeTo(Root / "helium" / "laika-helium.epub.css"))
      }
    }.mkString("", "\n\n", "\n\n")
    embeddedFonts + generate(settings, Nil, topBarHighContrast = false)
  }
  
  private def toVars (pairs: Seq[(String, String)]): Seq[(String, String)] = pairs.map { 
    case (name, value) => (s"--$name", value)
  }

  private def renderStyles (styles: Seq[(String, String)], darkMode: Boolean = false): String = {
    val renderedStyles = styles.map { case (name, value) =>
      s"$name: $value;"
    }
    
    if (darkMode) renderedStyles.mkString(s"$darkModeMediaQuery\n  :root {\n    ", "\n    ", "\n  }\n}\n\n")
    else renderedStyles.mkString(":root {\n  ", "\n  ", "\n}\n\n")
  }
  
  def colorSet (colors: ColorSet, topBarHighContrast: Boolean): Seq[(String, String)] = {
    import colors._
    def ref (name: String): String = s"var(--$name)"
    Seq(
      "primary-color" -> theme.primary.displayValue,
      "primary-light" -> theme.primaryLight.displayValue,
      "primary-medium" -> theme.primaryMedium.displayValue,
      "secondary-color" -> theme.secondary.displayValue,
      "text-color" -> theme.text.displayValue,
      "bg-color" -> theme.background.displayValue,
      "gradient-top" -> theme.bgGradient._1.displayValue,
      "gradient-bottom" -> theme.bgGradient._2.displayValue,
      "top-color" ->  (if (topBarHighContrast) ref("primary-light") else ref("primary-color")),
      "top-bg" ->     (if (topBarHighContrast) ref("primary-color") else ref("primary-light")),
      "top-hover" ->  (if (topBarHighContrast) ref("bg-color") else ref("secondary-color")),
      "top-border" -> (if (topBarHighContrast) ref("bg-color") else ref("primary-medium")),
      "messages-info" -> messages.info.displayValue,
      "messages-info-light" -> messages.infoLight.displayValue,
      "messages-warning" -> messages.warning.displayValue,
      "messages-warning-light" -> messages.warningLight.displayValue,
      "messages-error" -> messages.error.displayValue,
      "messages-error-light" -> messages.errorLight.displayValue,
      "syntax-base1" -> syntaxHighlighting.base.c1.displayValue,
      "syntax-base2" -> syntaxHighlighting.base.c2.displayValue,
      "syntax-base3" -> syntaxHighlighting.base.c3.displayValue,
      "syntax-base4" -> syntaxHighlighting.base.c4.displayValue,
      "syntax-base5" -> syntaxHighlighting.base.c5.displayValue,
      "syntax-wheel1" -> syntaxHighlighting.wheel.c1.displayValue,
      "syntax-wheel2" -> syntaxHighlighting.wheel.c2.displayValue,
      "syntax-wheel3" -> syntaxHighlighting.wheel.c3.displayValue,
      "syntax-wheel4" -> syntaxHighlighting.wheel.c4.displayValue,
      "syntax-wheel5" -> syntaxHighlighting.wheel.c5.displayValue
    )
  }

  def generate (common: DarkModeSupport, additionalVars: Seq[(String, String)], topBarHighContrast: Boolean): String = {
    import common._
    val vars = 
      colorSet(common.colors, topBarHighContrast) ++ 
      Seq(
        "body-font" -> ("\"" + themeFonts.body + "\", sans-serif"),
        "header-font" -> ("\"" + themeFonts.headlines + "\", sans-serif"),
        "code-font" -> ("\"" + themeFonts.code + "\", monospace"),
        "body-font-size" -> fontSizes.body.displayValue,
        "code-font-size" -> fontSizes.code.displayValue,
        "small-font-size" -> fontSizes.small.displayValue,
        "title-font-size" -> fontSizes.title.displayValue,
        "header2-font-size" -> fontSizes.header2.displayValue,
        "header3-font-size" -> fontSizes.header3.displayValue,
        "header4-font-size" -> fontSizes.header4.displayValue,
        "block-spacing" -> common.layout.defaultBlockSpacing.displayValue,
        "line-height" -> common.layout.defaultLineHeight.toString
      ) ++ 
      additionalVars
      
    val (colorScheme, darkModeStyles) = common.darkMode match {
      case Some(darkModeColors) => (Seq(("color-scheme", "light dark")), 
                                    renderStyles(toVars(colorSet(darkModeColors, topBarHighContrast)), darkMode = true))
      case None                 => (Nil, "")
    }
    
    renderStyles(toVars(vars) ++ colorScheme) + darkModeStyles
  }
  
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy