From 049e6078fc8b47100820ac35508afd23813ea9fe Mon Sep 17 00:00:00 2001
From: Joris
Date: Sun, 22 Jan 2017 21:17:17 +0100
Subject: Adapt applicatio to mobiles
---
 src/main/resources/index-dev.html                  |   1 +
 src/main/scala/reading/Media.scala                 |  12 +++
 src/main/scala/reading/component/Index.scala       |  24 ++---
 .../scala/reading/component/index/BookDetail.scala |   3 +-
 src/main/scala/reading/component/index/Books.scala |   1 +
 .../scala/reading/component/index/Filters.scala    |  41 --------
 .../reading/component/index/FiltersMenu.scala      |  72 --------------
 .../scala/reading/component/index/Header.scala     |  76 +++++++++++++++
 src/main/scala/reading/component/index/Menu.scala  | 104 +++++++++++++++++++++
 .../reading/component/index/style/BookDetail.scala |  12 ++-
 .../reading/component/index/style/Books.scala      |  11 ++-
 .../reading/component/index/style/Count.scala      |  20 ++++
 .../reading/component/index/style/Filters.scala    |  34 -------
 .../component/index/style/FiltersMenu.scala        |  28 ------
 .../reading/component/index/style/Header.scala     |  80 ++++++++++++++++
 .../scala/reading/component/index/style/Menu.scala |  97 +++++++++++++++++++
 src/main/scala/reading/component/style/Color.scala |  53 ++++++++---
 .../scala/reading/component/style/Global.scala     |  24 ++++-
 src/main/scala/reading/component/style/Index.scala |  16 +---
 .../scala/reading/component/widget/Popup.scala     |   4 +-
 .../reading/component/widget/style/Popup.scala     |  38 +++++---
 21 files changed, 508 insertions(+), 243 deletions(-)
 create mode 100644 src/main/scala/reading/Media.scala
 delete mode 100644 src/main/scala/reading/component/index/Filters.scala
 delete mode 100644 src/main/scala/reading/component/index/FiltersMenu.scala
 create mode 100644 src/main/scala/reading/component/index/Header.scala
 create mode 100644 src/main/scala/reading/component/index/Menu.scala
 create mode 100644 src/main/scala/reading/component/index/style/Count.scala
 delete mode 100644 src/main/scala/reading/component/index/style/Filters.scala
 delete mode 100644 src/main/scala/reading/component/index/style/FiltersMenu.scala
 create mode 100644 src/main/scala/reading/component/index/style/Header.scala
 create mode 100644 src/main/scala/reading/component/index/style/Menu.scala
(limited to 'src/main')
diff --git a/src/main/resources/index-dev.html b/src/main/resources/index-dev.html
index f03bafa..daba6e8 100644
--- a/src/main/resources/index-dev.html
+++ b/src/main/resources/index-dev.html
@@ -3,6 +3,7 @@
 
   
     
+    
     Conseils de lecture
     
     
diff --git a/src/main/scala/reading/Media.scala b/src/main/scala/reading/Media.scala
new file mode 100644
index 0000000..b9294e2
--- /dev/null
+++ b/src/main/scala/reading/Media.scala
@@ -0,0 +1,12 @@
+package reading
+
+import scalacss.Defaults._
+import scalacss.internal.DslBase.ToStyle
+
+object Media extends StyleSheet.Inline {
+  import dsl._
+
+  def desktop(st: ToStyle*): ToStyle = media.minWidth(1000.px)(st: _*)
+
+  def mobile(st: ToStyle*): ToStyle = media.maxWidth(1000.px)(st: _*)
+}
diff --git a/src/main/scala/reading/component/Index.scala b/src/main/scala/reading/component/Index.scala
index 8c2fdd6..57f5b4b 100644
--- a/src/main/scala/reading/component/Index.scala
+++ b/src/main/scala/reading/component/Index.scala
@@ -9,9 +9,8 @@ import scalacss.ScalatagsCss._
 
 import reading.Books
 import reading.component.style.{ Index => IndexStyle }
-import reading.component.index.{ FiltersMenu, Filters, Books => BooksComponent }
+import reading.component.index.{ Menu, Header, Books => BooksComponent }
 import reading.models.{ Book, Filter }
-import reading.utils.RxTag
 
 object Index {
   def apply(initialFilters: Seq[Filter]): HtmlTag = {
@@ -20,25 +19,16 @@ object Index {
       if (filters().isEmpty) Books() else Book.filter(Books(), filters())
     }
     val count: Rx[Int] = Rx(books().length)
+    val showFiltersMenu: Var[Boolean] = Var(false)
 
     div(
       IndexStyle.render,
-
-      div(
-        IndexStyle.header,
-        RxTag { implicit context =>
-          span(s"Conseils de lecture, ${count()} livre${if (count() > 1) "s" else ""}")
-        }
-      ),
-
+      IndexStyle.page,
+      Menu(books, filters, showFiltersMenu),
       div(
-        IndexStyle.page,
-        FiltersMenu(books, filters),
-        div(
-          IndexStyle.main,
-          Filters(filters),
-          BooksComponent(books)
-        )
+        IndexStyle.main,
+        Header(filters, showFiltersMenu, count),
+        BooksComponent(books)
       )
     )
   }
diff --git a/src/main/scala/reading/component/index/BookDetail.scala b/src/main/scala/reading/component/index/BookDetail.scala
index 6a9d83a..80280ac 100644
--- a/src/main/scala/reading/component/index/BookDetail.scala
+++ b/src/main/scala/reading/component/index/BookDetail.scala
@@ -15,7 +15,8 @@ object BookDetail {
 
       img(
         BookStyle.cover,
-        src := s"cover/${book.title}.jpg"
+        src := s"cover/${book.title}.jpg",
+        alt := book.title
       ),
 
       div(
diff --git a/src/main/scala/reading/component/index/Books.scala b/src/main/scala/reading/component/index/Books.scala
index 5d7c92e..b75a5b0 100644
--- a/src/main/scala/reading/component/index/Books.scala
+++ b/src/main/scala/reading/component/index/Books.scala
@@ -30,6 +30,7 @@ object Books {
                 img(
                   BooksStyle.cover,
                   src := s"cover/${book.title}.jpg",
+                  alt := book.title,
                   RxAttr(onclick, Rx(() => focus() = Some(book)))
                 )
               )
diff --git a/src/main/scala/reading/component/index/Filters.scala b/src/main/scala/reading/component/index/Filters.scala
deleted file mode 100644
index 569bd86..0000000
--- a/src/main/scala/reading/component/index/Filters.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-package reading.component.index
-
-import rx._
-import Ctx.Owner.Unsafe._
-
-import scalatags.JsDom.all._
-import scalacss.Defaults._
-import scalacss.ScalatagsCss._
-
-import reading.component.index.style.{ Filters => FiltersStyle }
-import reading.component.widget.Cross
-import reading.component.style.Col
-import reading.models.Filter
-import reading.Route
-import reading.utils.{ RxTag, RxAttr }
-
-object Filters {
-  def apply(filters: Var[Seq[Filter]]): Frag =
-    RxTag { implicit context =>
-      if (filters().isEmpty)
-        span("")
-      else
-        div(
-          FiltersStyle.render,
-          FiltersStyle.filters,
-
-          filters().sortBy(_.name).map { filter =>
-            div(
-              FiltersStyle.filter,
-              RxAttr(onclick, Rx(() => {
-                val newFilters = Filter.remove(filters(), filter)
-                filters() = newFilters
-                Route.push(Route.Books(newFilters))
-              })),
-              span(FiltersStyle.name, filter.name.capitalize),
-              Cross(15.px, Col.black)
-            )
-          }
-        )
-    }
-}
diff --git a/src/main/scala/reading/component/index/FiltersMenu.scala b/src/main/scala/reading/component/index/FiltersMenu.scala
deleted file mode 100644
index f3f9ca2..0000000
--- a/src/main/scala/reading/component/index/FiltersMenu.scala
+++ /dev/null
@@ -1,72 +0,0 @@
-package reading.component.index
-
-import rx._
-import Ctx.Owner.Unsafe._
-
-import scalatags.JsDom.all._
-import scalacss.Defaults._
-import scalacss.ScalatagsCss._
-
-import reading.component.index.style.{ FiltersMenu => FiltersMenuStyle }
-import reading.models._
-import reading.utils.{ RxTag, RxAttr }
-import reading.Route
-
-object FiltersMenu {
-  def apply(books: Rx[Seq[Book]], filters: Var[Seq[Filter]]): Frag =
-    RxTag { implicit context =>
-      div(
-        FiltersMenuStyle.render,
-        FiltersMenuStyle.groups,
-        filters().find(_.kind == FilterKind.Grade) match {
-          case None =>
-            group(books, filters, "Classe", Grade.values.map(Filter.apply(_)))
-          case Some(grade) =>
-            group(books, filters, "Programme", Program.values.map(Filter.apply(_)))
-        },
-        group(books, filters, "Theme", Theme.values.map(Filter.apply(_))),
-        group(books, filters, "Genre", Genre.values.map(Filter.apply(_))),
-        group(books, filters, "Niveau", Level.values.map(Filter.apply(_))),
-        group(books, filters, "Période", Period.values.map(Filter.apply(_)))
-      )
-    }
-
-  def group(
-    books: Rx[Seq[Book]],
-    filters: Var[Seq[Filter]],
-    name: String,
-    groupFilters: Seq[Filter]
-  )(
-    implicit
-    context: Ctx.Data
-  ): Frag = {
-    val filtersWithCount = Rx {
-      groupFilters
-        .filter(filter => !Filter.contains(filters(), filter))
-        .map(filter => (filter, Book.filter(books(), Seq(filter)).length))
-        .filter(_._2 > 0)
-    }
-
-    if (filtersWithCount().isEmpty)
-      span("")
-    else
-      div(
-        FiltersMenuStyle.group,
-        div(FiltersMenuStyle.groupTitle, name),
-        div(
-          filtersWithCount().map {
-            case (filter, count) =>
-              button(
-                FiltersMenuStyle.filter,
-                RxAttr(onclick, Rx(() => {
-                  val newFilters = filter +: filters()
-                  filters() = newFilters
-                  Route.push(Route.Books(newFilters))
-                })),
-                span(s"${filter.name.capitalize} ($count)")
-              )
-          }
-        )
-      )
-  }
-}
diff --git a/src/main/scala/reading/component/index/Header.scala b/src/main/scala/reading/component/index/Header.scala
new file mode 100644
index 0000000..7e29b4c
--- /dev/null
+++ b/src/main/scala/reading/component/index/Header.scala
@@ -0,0 +1,76 @@
+package reading.component.index
+
+import org.scalajs.dom
+
+import rx._
+import Ctx.Owner.Unsafe._
+
+import scalatags.JsDom.all._
+import scalacss.Defaults._
+import scalacss.ScalatagsCss._
+
+import reading.component.index.style.{ Header => HeaderStyle }
+import reading.component.widget.Cross
+import reading.component.style.{ Color => C }
+import reading.models.Filter
+import reading.Route
+import reading.utils.{ RxTag, RxAttr }
+
+object Header {
+  def apply(filters: Var[Seq[Filter]], showFiltersMenu: Var[Boolean], booksCount: Rx[Int]): Frag = {
+    val filtersCount: Rx[Int] = Rx(filters().length)
+
+    div(
+      HeaderStyle.render,
+      HeaderStyle.header,
+
+      RxTag { implicit context =>
+        div(
+          div(
+            HeaderStyle.showFiltersMenu,
+            RxAttr(onclick, Rx(() => {
+              showFiltersMenu() = true
+              dom.document.body.style.overflowY = "hidden"
+            })),
+            "Filtrer",
+            if (filtersCount() > 0) span(HeaderStyle.filtersCount, filtersCount()) else span("")
+          ),
+
+          if (filters().isEmpty)
+            span("")
+          else
+            div(
+              HeaderStyle.filters,
+
+              div(
+                HeaderStyle.clear,
+                RxAttr(onclick, Rx(() => updateFilters(filters, Nil))),
+                "Effacer les filtres"
+              ),
+
+              filters().sortBy(_.name).map { filter =>
+                div(
+                  HeaderStyle.filter,
+                  RxAttr(onclick, Rx(() => updateFilters(filters, Filter.remove(filters(), filter)))),
+                  span(HeaderStyle.name, filter.name.capitalize),
+                  Cross(15.px, C.black.value)
+                )
+              }
+            )
+        )
+      },
+
+      RxTag { implicit context =>
+        div(
+          HeaderStyle.booksCount,
+          span(s"${booksCount()} livre${if (booksCount() > 1) "s" else ""}")
+        )
+      }
+    )
+  }
+
+  private def updateFilters(filters: Var[Seq[Filter]], newFilters: Seq[Filter]): Unit = {
+    filters() = newFilters
+    Route.push(Route.Books(newFilters))
+  }
+}
diff --git a/src/main/scala/reading/component/index/Menu.scala b/src/main/scala/reading/component/index/Menu.scala
new file mode 100644
index 0000000..5ffbd25
--- /dev/null
+++ b/src/main/scala/reading/component/index/Menu.scala
@@ -0,0 +1,104 @@
+package reading.component.index
+
+import org.scalajs.dom
+
+import rx._
+import Ctx.Owner.Unsafe._
+
+import scalatags.JsDom.all._
+import scalacss.Defaults._
+import scalacss.ScalatagsCss._
+
+import reading.component.index.style.{ Menu => MenuStyle }
+import reading.models._
+import reading.utils.{ RxTag, RxAttr }
+import reading.Route
+
+object Menu {
+  def apply(books: Rx[Seq[Book]], filters: Var[Seq[Filter]], showFiltersMenu: Var[Boolean]): Frag =
+    RxTag { implicit context =>
+      div(
+        MenuStyle.render,
+        if (showFiltersMenu()) MenuStyle.show else "",
+        MenuStyle.menu,
+
+        header(showFiltersMenu, filters().length),
+
+        filters().find(_.kind == FilterKind.Grade) match {
+          case None => group(books, filters, "Classe", Grade.values.map(Filter.apply(_)))
+          case Some(grade) => group(books, filters, grade.name, Program.values.map(Filter.apply(_)), Some(grade))
+        },
+        group(books, filters, "Theme", Theme.values.map(Filter.apply(_))),
+        group(books, filters, "Genre", Genre.values.map(Filter.apply(_))),
+        group(books, filters, "Niveau", Level.values.map(Filter.apply(_))),
+        group(books, filters, "Période", Period.values.map(Filter.apply(_)))
+      )
+    }
+
+  def header(showFiltersMenu: Var[Boolean], count: Int): HtmlTag =
+    div(
+      MenuStyle.header,
+      span(
+        MenuStyle.close,
+        RxAttr(onclick, Rx(() => {
+          showFiltersMenu() = false
+          dom.document.body.style.overflowY = "scroll"
+        }))
+      ),
+      "Filtrer",
+      if (count > 0) span(MenuStyle.count, count) else span("")
+    )
+
+  def group(
+    books: Rx[Seq[Book]],
+    filters: Var[Seq[Filter]],
+    name: String,
+    groupFilters: Seq[Filter],
+    parentFilter: Option[Filter] = None
+  )(
+    implicit
+    context: Ctx.Data
+  ): Frag = {
+    val filtersWithCount = Rx {
+      groupFilters
+        .map(filter => (filter, Book.filter(books(), Seq(filter)).length))
+        .filter(_._2 > 0)
+    }
+
+    if (filtersWithCount().isEmpty)
+      span("")
+    else
+      div(
+        MenuStyle.filterGroup,
+        div(
+          MenuStyle.filterTitle,
+          parentFilter.map(filter =>
+            RxAttr(onclick, Rx(() => updateFilters(filters, Filter.remove(filters(), filter))))).getOrElse(""),
+          if (parentFilter.isDefined) MenuStyle.activeFilter else "",
+          name
+        ),
+        div(
+          filtersWithCount().map {
+            case (filter, count) => {
+              val isActive = Filter.contains(filters(), filter)
+
+              button(
+                MenuStyle.filter,
+                if (isActive) MenuStyle.activeFilter else "",
+                RxAttr(onclick, Rx(() => updateFilters(
+                  filters,
+                  if (isActive) Filter.remove(filters(), filter) else filter +: filters()
+                ))),
+                span(s"${filter.name.capitalize} ($count)")
+              )
+            }
+          }
+        )
+      )
+  }
+
+  private def updateFilters(filters: Var[Seq[Filter]], newFilters: Seq[Filter]): Unit = {
+    filters() = newFilters
+    Route.push(Route.Books(newFilters))
+  }
+}
diff --git a/src/main/scala/reading/component/index/style/BookDetail.scala b/src/main/scala/reading/component/index/style/BookDetail.scala
index e54c964..6e79113 100644
--- a/src/main/scala/reading/component/index/style/BookDetail.scala
+++ b/src/main/scala/reading/component/index/style/BookDetail.scala
@@ -2,7 +2,8 @@ package reading.component.index.style
 
 import scalacss.Defaults._
 
-import reading.component.style.Col
+import reading.Media
+import reading.component.style.{ Color => C }
 
 object BookDetail extends StyleSheet.Inline {
   import dsl._
@@ -12,7 +13,7 @@ object BookDetail extends StyleSheet.Inline {
     fontSize(18.px),
     fontWeight.bold,
     marginBottom(20.px),
-    color(Col.congoBrown)
+    color(C.congoBrown.value)
   )
 
   val author = style(
@@ -20,12 +21,15 @@ object BookDetail extends StyleSheet.Inline {
   )
 
   val detail = style(
-    display.flex
+    display.flex,
+    flexWrap.wrap,
+    justifyContent.center
   )
 
   val cover = style(
     height(300.px),
-    marginRight(30.px)
+    marginRight(30.px),
+    Media.mobile(marginBottom(30.px))
   )
 
   val item = style(
diff --git a/src/main/scala/reading/component/index/style/Books.scala b/src/main/scala/reading/component/index/style/Books.scala
index 34a7a83..79aea1a 100644
--- a/src/main/scala/reading/component/index/style/Books.scala
+++ b/src/main/scala/reading/component/index/style/Books.scala
@@ -2,22 +2,27 @@ package reading.component.index.style
 
 import scalacss.Defaults._
 
+import reading.Media
+
 object Books extends StyleSheet.Inline {
   import dsl._
 
   val books = style(
     display.flex,
-    flexWrap.wrap
+    flexWrap.wrap,
+    justifyContent.center
   )
 
   val book = style(
-    width(250.px),
+    Media.desktop(width(250.px)),
+    Media.mobile(width(150.px)),
     paddingBottom(60.px),
     textAlign.center
   )
 
   val cover = style(
-    height(250.px),
+    Media.desktop(height(250.px)),
+    Media.mobile(height(150.px)),
     cursor.pointer
   )
 }
diff --git a/src/main/scala/reading/component/index/style/Count.scala b/src/main/scala/reading/component/index/style/Count.scala
new file mode 100644
index 0000000..cc1eb80
--- /dev/null
+++ b/src/main/scala/reading/component/index/style/Count.scala
@@ -0,0 +1,20 @@
+package reading.component.index.style
+
+import scalacss.Defaults._
+
+import reading.component.style.{ Color => C }
+
+object Count extends StyleSheet.Inline {
+  import dsl._
+
+  val count = style(
+    display.flex,
+    alignItems.center,
+    justifyContent.center,
+    backgroundColor(C.stiletto.value),
+    width(25.px),
+    height(25.px),
+    borderRadius(50.%%),
+    marginLeft(20.px)
+  )
+}
diff --git a/src/main/scala/reading/component/index/style/Filters.scala b/src/main/scala/reading/component/index/style/Filters.scala
deleted file mode 100644
index c324965..0000000
--- a/src/main/scala/reading/component/index/style/Filters.scala
+++ /dev/null
@@ -1,34 +0,0 @@
-package reading.component.index.style
-
-import scalacss.Defaults._
-
-import reading.component.style.Col
-
-object Filters extends StyleSheet.Inline {
-  import dsl._
-
-  val filters = style(
-    marginBottom(50.px),
-    display.flex
-  )
-
-  val filter = style(
-    display.flex,
-    alignItems.center,
-    padding(15.px),
-    marginRight(20.px),
-    borderRadius(2.px),
-    &.hover(cursor.pointer),
-    border(1.px, solid, Col.alto),
-    fontSize(18.px)
-  )
-
-  val name = style(
-    marginRight(10.px)
-  )
-
-  val cross = style(
-    width(15.px),
-    height(15.px)
-  )
-}
diff --git a/src/main/scala/reading/component/index/style/FiltersMenu.scala b/src/main/scala/reading/component/index/style/FiltersMenu.scala
deleted file mode 100644
index 3fcc363..0000000
--- a/src/main/scala/reading/component/index/style/FiltersMenu.scala
+++ /dev/null
@@ -1,28 +0,0 @@
-package reading.component.index.style
-
-import scalacss.Defaults._
-
-import reading.component.style.Col
-
-object FiltersMenu extends StyleSheet.Inline {
-  import dsl._
-
-  val groups = style(
-    width(280.px)
-  )
-
-  val group = style(
-    marginBottom(30.px)
-  )
-
-  val groupTitle = style(
-    color(Col.congoBrown),
-    fontWeight.bold,
-    textTransform.uppercase,
-    padding(10.px, 30.px, 15.px)
-  )
-
-  val filter = style(
-    padding(10.px, 30.px)
-  )
-}
diff --git a/src/main/scala/reading/component/index/style/Header.scala b/src/main/scala/reading/component/index/style/Header.scala
new file mode 100644
index 0000000..643dd8e
--- /dev/null
+++ b/src/main/scala/reading/component/index/style/Header.scala
@@ -0,0 +1,80 @@
+package reading.component.index.style
+
+import scalacss.Defaults._
+
+import reading.Media
+import reading.component.style.{ Color => C }
+
+object Header extends StyleSheet.Inline {
+  import dsl._
+
+  val header = style(
+    Media.desktop(margin(40.px)),
+    Media.mobile(margin(30.px))
+  )
+
+  val showFiltersMenu = style(
+    Media.desktop(display.none),
+    Media.mobile(
+      display.flex,
+      justifyContent.center,
+      alignItems.center,
+      width(100.%%),
+      backgroundColor(C.englishWalnut.value),
+      color(C.white.value),
+      border(1.px, solid, C.congoBrown.value),
+      borderRadius(4.px),
+      marginBottom(20.px),
+      padding(10.px, 0.px),
+      cursor.pointer
+    )
+  )
+
+  val filtersCount = style(
+    Count.count
+  )
+
+  val filters = style(
+    display.flex,
+    marginBottom(30.px),
+    Media.mobile(display.none)
+  )
+
+  private val box = style(
+    display.flex,
+    alignItems.center,
+    padding(15.px),
+    marginRight(20.px),
+    borderRadius(2.px),
+    border(1.px, solid, C.gray.lighten(80).value),
+    fontSize(18.px),
+    &.hover(cursor.pointer)
+  )
+
+  val clear = style(
+    box,
+    backgroundColor(C.mickado.value),
+    color(C.white.value),
+    &.hover(backgroundColor(C.mickado.lighten(20).value))
+  )
+
+  val filter = style(
+    box,
+    &.hover(borderColor(C.gray.lighten(50).value))
+  )
+
+  val name = style(
+    marginRight(10.px)
+  )
+
+  val cross = style(
+    width(15.px),
+    height(15.px)
+  )
+
+  val booksCount = style(
+    fontSize(20.px),
+    color(C.gray.value),
+    Media.mobile(textAlign.center)
+  )
+}
diff --git a/src/main/scala/reading/component/index/style/Menu.scala b/src/main/scala/reading/component/index/style/Menu.scala
new file mode 100644
index 0000000..38620e8
--- /dev/null
+++ b/src/main/scala/reading/component/index/style/Menu.scala
@@ -0,0 +1,97 @@
+package reading.component.index.style
+
+import scalacss.Defaults._
+
+import reading.Media
+import reading.component.style.{ Color => C }
+
+object Menu extends StyleSheet.Inline {
+  import dsl._
+
+  val menu = style(
+    Media.mobile(display.none),
+    backgroundColor(C.englishWalnut.value),
+    color(C.white.value),
+    width(280.px),
+    height(100.%%),
+    boxShadow := "4px 0px 6px -1px rgba(0, 0, 0, 0.2)"
+  )
+
+  val header = style(
+    display.flex,
+    alignItems.center,
+    justifyContent.center,
+    position.relative,
+    height(60.px),
+    backgroundColor(C.englishWalnut.darken(20).value),
+    color(C.white.value),
+    textTransform.uppercase,
+    fontWeight.bold,
+    letterSpacing(1.px),
+    marginBottom(20.px),
+    Media.mobile(boxShadow := "0px 3px 5px -1px rgba(0, 0, 0, 0.2)")
+  )
+
+  val close = style(
+    Media.desktop(display.none),
+    Media.mobile(
+      position.absolute,
+      top(0.px),
+      left(0.px),
+      width(100.%%),
+      height(100.%%),
+      cursor.pointer
+    )
+  )
+
+  val count = style(
+    Count.count
+  )
+
+  val show = style(
+    Media.mobile(
+      display.block,
+      position.fixed,
+      top(0.px),
+      left(0.px),
+      width(100.%%),
+      height(100.%%),
+      zIndex(1),
+      overflowY.scroll,
+      backgroundColor(C.white.value),
+      color(C.black.value)
+    )
+  )
+
+  val filterGroup = style()
+
+  private val filterCommon = style(
+    display.flex,
+    alignItems.center,
+    width(100.%%),
+    padding(5.px, 30.px),
+    textAlign.left
+  )
+
+  val filterTitle = style(
+    filterCommon,
+    minHeight(50.px),
+    fontWeight.bold,
+    textTransform.uppercase,
+    letterSpacing(1.px),
+    marginBottom(10.px)
+  )
+
+  val filter = style(
+    filterCommon,
+    marginLeft(10.px),
+    minHeight(40.px),
+    color.inherit,
+    &.lastChild(marginBottom(30.px))
+  )
+
+  val activeFilter = style(
+    color(C.stiletto.value),
+    cursor.pointer
+  )
+}
diff --git a/src/main/scala/reading/component/style/Color.scala b/src/main/scala/reading/component/style/Color.scala
index 85498de..1f44eb5 100644
--- a/src/main/scala/reading/component/style/Color.scala
+++ b/src/main/scala/reading/component/style/Color.scala
@@ -1,16 +1,47 @@
 package reading.component.style
 
-import scalacss.Defaults._
+import scala.util.Try
+
+import scalacss.internal.{ ValueT, Color => ScalaCssColor }
+
+case class Color(red: Int, green: Int, blue: Int, alpha: Double = 1.0) {
+  val value: ValueT[ValueT.Color] = ScalaCssColor.rgba(red, green, blue, alpha)
+
+  def lighten(n: Int): Color =
+    Color(
+      red = Color.bound(red + n),
+      green = Color.bound(green + n),
+      blue = Color.bound(blue + n),
+      alpha = alpha
+    )
+
+  def darken(n: Int): Color =
+    Color(
+      red = Color.bound(red - n),
+      green = Color.bound(green - n),
+      blue = Color.bound(blue - n),
+      alpha = alpha
+    )
+}
 
 // http://chir.ag/projects/name-that-color
-object Col extends StyleSheet.Inline {
-  import dsl._
-
-  val black = c"#000000"
-  val white = c"#FFFFFF"
-  val gray = c"#7E7E7E"
-  val alto = c"#D4D1D1"
-  val eastBay = c"#505080"
-  val congoBrown = c"#57363E"
-  val stiletto = c"#9C3336"
+object Color {
+  val black = Color.fromHex("#000000")
+  val mickado = Color.fromHex("#2D2510")
+  val white = Color.fromHex("#FFFFFF")
+  val gray = Color.fromHex("#808080")
+  val eastBay = Color.fromHex("#505080")
+  val congoBrown = Color.fromHex("#57363E")
+  val stiletto = Color.fromHex("#9C3336")
+  val englishWalnut = Color.fromHex("#3F2626")
+
+  def fromHex(hex: String, alpha: Double = 1.0) =
+    Color(
+      red = Try(Integer.parseInt(hex.slice(1, 3), 16)).getOrElse(0),
+      green = Try(Integer.parseInt(hex.slice(3, 5), 16)).getOrElse(0),
+      blue = Try(Integer.parseInt(hex.slice(5, 7), 16)).getOrElse(0),
+      alpha = alpha
+    )
+
+  def bound(n: Int): Int = Math.max(0, Math.min(255, n))
 }
diff --git a/src/main/scala/reading/component/style/Global.scala b/src/main/scala/reading/component/style/Global.scala
index 861b78a..db7ef34 100644
--- a/src/main/scala/reading/component/style/Global.scala
+++ b/src/main/scala/reading/component/style/Global.scala
@@ -2,27 +2,43 @@ package reading.component.style
 
 import scalacss.Defaults._
 
+import reading.component.style.{ Color => C }
+
 object Global extends StyleSheet.Standalone {
   import dsl._
 
-  "html" -
+  "html" - (
     boxSizing.borderBox
+  )
+
+  "body" - (
+    position.absolute,
+    top(0.px),
+    right(0.px),
+    bottom(0.px),
+    left(0.px)
+  )
+
+  "body > *" - (
+    minHeight(100.%%)
+  )
 
   "a" - (
-    color(Col.eastBay),
+    color(C.eastBay.value),
     &.hover(
       textDecoration := "underline"
     )
   )
 
-  "*, *:before, *:after" -
+  "*, *:before, *:after" - (
     boxSizing.inherit
+  )
 
   "button" - (
     cursor.pointer,
     display.flex,
     backgroundColor(initial),
-    color(Col.black),
+    color(C.black.value),
     border.none
   )
 }
diff --git a/src/main/scala/reading/component/style/Index.scala b/src/main/scala/reading/component/style/Index.scala
index 8f31683..9f1d261 100644
--- a/src/main/scala/reading/component/style/Index.scala
+++ b/src/main/scala/reading/component/style/Index.scala
@@ -5,22 +5,12 @@ import scalacss.Defaults._
 object Index extends StyleSheet.Inline {
   import dsl._
 
-  val header = style(
-    fontSize(40.px),
-    color(Col.congoBrown),
-    textAlign.center,
-    margin(10.px, auto, 30.px),
-    padding(20.px),
-    &.hover(
-      textDecoration := "none"
-    )
-  )
-
   val page = style(
-    display.flex
+    display.flex,
+    minHeight(inherit)
   )
 
   val main = style(
-    marginLeft(20.px)
+    width(100.%%)
   )
 }
diff --git a/src/main/scala/reading/component/widget/Popup.scala b/src/main/scala/reading/component/widget/Popup.scala
index 67e6a4c..f47bc1c 100644
--- a/src/main/scala/reading/component/widget/Popup.scala
+++ b/src/main/scala/reading/component/widget/Popup.scala
@@ -10,7 +10,7 @@ import scalacss.Defaults._
 import scalacss.ScalatagsCss._
 
 import reading.component.widget.style.{ Popup => PopupStyle }
-import reading.component.style.Col
+import reading.component.style.{ Color => C }
 import reading.utils.{ RxAttr }
 
 object Popup {
@@ -32,7 +32,7 @@ object Popup {
         button(
           PopupStyle.cross,
           RxAttr(onclick, Rx(() => close(onClose))),
-          Cross(20.px, Col.stiletto)
+          Cross(20.px, C.stiletto.value)
         )
       )
     )
diff --git a/src/main/scala/reading/component/widget/style/Popup.scala b/src/main/scala/reading/component/widget/style/Popup.scala
index 422e3bd..ea67d9e 100644
--- a/src/main/scala/reading/component/widget/style/Popup.scala
+++ b/src/main/scala/reading/component/widget/style/Popup.scala
@@ -2,7 +2,8 @@ package reading.component.widget.style
 
 import scalacss.Defaults._
 
-import reading.component.style.Col
+import reading.Media
+import reading.component.style.{ Color => C }
 
 object Popup extends StyleSheet.Inline {
   import dsl._
@@ -21,22 +22,33 @@ object Popup extends StyleSheet.Inline {
   )
 
   val curtain = style(
-    width(100.%%),
-    height(100.%%),
-    position.absolute,
-    top(0.px),
-    left(0.px),
-    backgroundColor(Col.black),
-    opacity(0.5),
-    cursor.pointer
+    Media.desktop(
+      width(100.%%),
+      height(100.%%),
+      position.absolute,
+      top(0.px),
+      left(0.px),
+      backgroundColor(C.black.value),
+      opacity(0.5),
+      cursor.pointer
+    ),
+    Media.mobile(
+      display.none
+    )
   )
 
   val content = style(
     position.relative,
-    width(50.%%),
-    backgroundColor(Col.white),
-    padding(30.px),
-    borderRadius(5.px)
+    backgroundColor(C.white.value),
+    Media.desktop(
+      width(50.%%),
+      borderRadius(5.px)
+    ),
+    Media.mobile(
+      width(100.%%),
+      height(100.%%)
+    ),
+    padding(30.px)
   )
 
   val cross = style(
-- 
cgit v1.2.3