Today we're going to take a break from the Single Page App we've been working on
to explore
style-elements,
which is a package for creating styles for elements, naturally enough. From the
README, The Style Elements library is a new set of primitives for working with
layout and style in Elm.
We'll use it on the elm-emoji-picker I've been
building for Firestorm. Let's get started.
We're starting with the dailydrip/elm-emoji-picker repo tagged before this
episode.
First, let's look at the app as it stands:
./run.sh
# Visit it in the browser
Here we have an emoji picker. It looks OK, but I'd like to iterate on the styles
a bit and it seemed like an easy way to get started with style-elements.
We'll start by installing the package:
elm-package install -y mdgriffith/style-elements
Right now we're using some CSS in this project. Let's remove it and stop loading it:
git rm style.css
vim index.html
# Then remove the stylesheet link tag
If we run the project again, we can see that the styles have been removed.
./run.sh
style-elements consists of Styles and Elements. We'll make a Styles
module of our own, creating a basic stylesheet with no applicable styles, as a
good basic starting point. We'll give a name to all of the things we want to
style eventually, in a union type.
vim Styles.elm
module Styles exposing (Styles(..), stylesheet)
import Style exposing (..)
{-| Here's where we define all of the named styles that exist in our app.
It's nice to have a blank style.
-}
type Styles
= None
| SelectedEmoji
| EmojiList
| Emoji
{-| Then, we define a stylesheet. This describes all of the properties that
aren't related to layout, position, or size.
-}
stylesheet : StyleSheet Styles variations
stylesheet =
Style.stylesheet
[ style None [] -- our None style adds no styles.
, style SelectedEmoji
[]
, style EmojiList
[]
, style Emoji
[]
]
Next, let's wire this into Main and replace our view with style-elements layout
functions to lay it out.
vim Main.elm
module Main exposing (..)
import Dict exposing (Dict)
-- Element is a module from style-elements, and the attributes and events
-- largely mirror those of elm-lang/html
import Element exposing (..)
import Element.Attributes exposing (..)
import Element.Events exposing (onClick, onInput)
import Emoji exposing (Emoji, emojis)
-- We only need the Html type itself now
-- And we don't need to import Html attributes or events
import Html exposing (Html)
import Ports
-- We'll also pull in our Styles so we can specify them on various elements
import Styles exposing (Styles(..))
-- ...
-- Then our view uses Element functions
view : Model -> Html Msg
view model =
let
-- ...
in
-- We use Element.root for the base of it, passing it the stylesheet that
-- should apply inside this area
Element.root Styles.stylesheet <|
-- We'll just lay everything out in a column for now
column None
[]
-- We'll include an element that's 400px wide and centered
[ el None [ center, width (px 400) ] <|
-- And inside of that element we'll lay things out in columns
-- again
column None
[]
-- We'll include an input...
[ inputText None
[ onInput UpdatePrefix
, placeholder "Search for an emoji"
]
model.searchString
-- ... our selected emoji
, el SelectedEmoji [] <| text selectedEmojiString
-- ... and the list of emoji we've filtered to
, viewEmojiList model.searchString
]
]
-- The list of emoji will just be a column for now
viewEmojiList : String -> Element Styles variation Msg
viewEmojiList searchPrefix =
let
-- ...
in
column EmojiList [] <|
List.map viewEmoji filteredEmoji
-- And an emoji is an element with the Emoji style
viewEmoji : ( String, Emoji ) -> Element Styles variation Msg
viewEmoji ( key, ( emojiString, emojiName, commonNames ) as emoji ) =
el Emoji [ onClick <| SelectEmoji emoji ] <| text emojiString
If we check out the app now, it's mostly workable. Next, I'd like to make the emoji have some padding, and change background color when hovered:
vim Styles.elm
module Styles exposing (Styles(..), stylesheet)
import Color exposing (rgba)
import Style exposing (..)
import Style.Color as Color
-- ...
colors =
{ lightGrey = rgba 230 230 230 1
, transparent = rgba 0 0 0 0
}
{-| Then, we define a stylesheet. This describes all of the properties that
aren't related to layout, position, or size.
-}
stylesheet : StyleSheet Styles variations
stylesheet =
Style.stylesheet
[ -- ...
, style Emoji
[ Color.background colors.transparent
, hover
[ Color.background colors.lightGrey
]
]
]
That takes care of the background color. Padding is a layout concern, so we add it on the element in the view:
vim Main.elm
-- ...
viewEmoji : ( String, Emoji ) -> Element Styles variation Msg
viewEmoji ( key, ( emojiString, emojiName, commonNames ) as emoji ) =
el Emoji
[ onClick <| SelectEmoji emoji
, padding 6
]
<|
text emojiString
Now if we look, we can see that as we hover the emoji they get a background color. This is still a pretty ugly experience though, so let's make rows of emoji in a grid, by using flexbox.
We'll turn the emojiList into a wrappedRow element, which will make the
children wrap as they outgrow the width of the box:
viewEmojiList : String -> Element Styles variation Msg
viewEmojiList searchPrefix =
let
-- ...
in
wrappedRow EmojiList [] <|
List.map viewEmoji filteredEmoji
Then in viewEmoji we'll specify a width and height, and center the emoji
inside:
viewEmoji : ( String, Emoji ) -> Element Styles variation Msg
viewEmoji ( key, ( emojiString, emojiName, commonNames ) as emoji ) =
el Emoji
[ onClick <| SelectEmoji emoji
, width (px 40)
, height (px 40)
]
<|
el None [ center, verticalCenter ] <|
text emojiString
Now it shows up as a nice grid. We can click on the emoji, so let's modify the styles to make the cursor a pointer when we hover them:
module Styles exposing (Styles(..), stylesheet)
-- ...
stylesheet : StyleSheet Styles variations
stylesheet =
Style.stylesheet
[ -- ...
, style Emoji
[ Color.background colors.transparent
, hover
[ Color.background colors.lightGrey
]
, cursor "pointer"
]
]
With that, things are looking alright. I think this is a fine place to stop.
In today's episode we implemented styles for our emoji picker using
style-elements. There's a lot to like about this package. A thing I don't
currently love is how wordy the resulting HTML is, as there are inline styles on
each element. However, there's a lot of work going on on this package and it's
very nice that it frees us from thinking of CSS directly in a lot of cases. Let
me know if you use it and build something cool. See you soon!