Internal.Model.Element msg
The basic building block of your layout. Here we create a
import Element
view =
Element.el [] (Element.text "Hello!")
none : Element msg
Nothing to see here!
text : String -> Element msg
Create some plain text.
text "Hello, you stylish developer!"
Note text does not wrap by default. In order to get text to wrap, check out paragraph
!
el : List (Attribute msg) -> Element msg -> Element msg
The basic building block of your layout.
You can think of an el
as a div
, but it can only hae one child.
If you want multiple children, you'll need to use something like row
or column
import Color exposing (blue, darkBlue)
import Element exposing (Element)
import Element.Background as Background
import Element.Border as Border
myElement : Element msg
myElement =
Element.el
[ Background.color blue
, Border.color darkBlue
]
(Element.text "You've made a stylish element!")
Rows and columns are the most common layouts.
row : List (Attribute msg) -> List (Element msg) -> Element msg
If you want a row of elements, use row
!
wrappedRow : List (Attribute msg) -> List (Element msg) -> Element msg
column : List (Attribute msg) -> List (Element msg) -> Element msg
Text needs it's own layout primitives.
paragraph : List (Attribute msg) -> List (Element msg) -> Element msg
A paragraph will layout all children as wrapped, inline elements.
import Element
import Element.Font as Font
Element.paragraph []
[ text "lots of text ...."
, el [ Font.bold ] (text "this is bold")
, text "lots of text ...."
]
This is really useful when you want to markup text by having some parts be bold, or some be links, or whatever you so desire.
Also, if a child element has alignLeft
or alignRight
, then it will be moved to that side and the text will flow around it, (ah yes, float
behavior).
This makes it particularly easy to do something like a dropped capital.
import Element
import Element.Font as Font
Element.paragraph []
[ el
[ alignLeft
, padding 5
, Font.lineHeight 1
]
(text "S")
, text "o much text ...."
]
Which will look something like
textColumn : List (Attribute msg) -> List (Element msg) -> Element msg
Now that we have a paragraph, we need some way to attach a bunch of paragraph's together.
To do that we can use a textColumn
.
The main difference between a column
and a textColumn
is that textColumn
will flow the text around elements that have alignRight
or alignLeft
, just like we just saw with paragraph.
In the following example, we have a textColumn
where one child has alignLeft
.
Element.textColumn [ spacing 10, padding 10 ]
[ paragraph [] [ text "lots of text ...." ]
, el [ alignLeft ] none
, paragraph [] [ text "lots of text ...." ]
]
Which will result in something like:
{ header : Element msg
, width : Length
, view : record -> Element msg
}
table : List (Attribute msg) -> { data : List records, columns : List (Column records msg) } -> Element msg
Show some tabular data.
Start with a list of records and specify how each column should be rendered.
So, if we have a list of persons
:
type alias Person =
{ firstName : String
, lastName : String
}
persons : List Person
persons =
[ { firstName = "David"
, lastName = "Bowie"
}
, { firstName = "Florence"
, lastName = "Welch"
}
]
We could render it using
Element.table []
{ data = persons
, columns =
[ { header = Element.text "First Name"
, width = fill
, view =
\person ->
Element.text person.firstName
}
, { header = Element.text "Last Name"
, width = fill
, view =
\person ->
Element.text person.lastName
}
]
}
Note: Sometimes you might not have a list of records directly in your model. In this case it can be really nice to write a function that transforms some part of your model into a list of records before feeding it into Element.table
.
{ header : Element msg
, width : Length
, view : Basics.Int -> record -> Element msg
}
indexedTable : List (Attribute msg) -> { data : List records, columns : List (IndexedColumn records msg) } -> Element msg
Same as Element.table
except the view
for each column will also receive the row index as well as the record.
Internal.Model.Attribute () msg
Standard attribute which cannot be a decoration.
width : Length -> Attribute msg
height : Length -> Attribute msg
Internal.Model.Length
px : Basics.Int -> Length
shrink : Length
Shrink an element to fit it's contents.
fill : Length
Fill the available space. The available space will be split evenly between elements that have width fill
.
fillPortion : Basics.Int -> Length
Sometimes you may not want to split available space evenly. In this case you can use fillPortion
to define which elements should have what portion of the available space.
So, two elements, one with width (fillPortion 2)
and one with width (fillPortion 3)
. The first would get 2 portions of the available space, while the second would get 3.
Also: fill == fillPortion 1
maximum : Basics.Int -> Length -> Length
Add a maximum to a length.
el
[ height
(fill
|> maximum 300
)
]
(text "I will stop at 300px")
minimum : Basics.Int -> Length -> Length
Similarly you can set a minimum boundary.
el
[ height
(fill
|> maximum 300
|> minimum 30
)
]
(text "I will stop at 300px")
explain : Todo -> Attribute msg
Highlight the borders of an element and it's children below. This can really help if you're running into some issue with your layout!
Note This attribute needs to be handed Debug.todo
in order to work, even though it won't do anything with it. This is a safety measure so you don't accidently ship code with explain
in it, as Elm won't compile with --optimize
if you still have a Debug
statement in your code.
el
[ Element.explain Debug.todo
]
(text "Help! I'm being debugged!")
There's no concept of margin in style-elements
, instead we have padding and spacing.
Padding is the distance between the outer edge and the content, and spacing is the space between children.
So, if we have the following row, with some padding and spacing.
Element.row [ padding 10, spacing 7 ]
[ Element.el [] none
, Element.el [] none
, Element.el [] none
]
Here's what we can expect:
padding : Basics.Int -> Attribute msg
paddingXY : Basics.Int -> Basics.Int -> Attribute msg
Set horizontal and vertical padding.
paddingEach : { top : Basics.Int, right : Basics.Int, bottom : Basics.Int, left : Basics.Int } -> Attribute msg
If you find yourself defining unique paddings all the time, you might consider defining
edges =
{ top = 0
, right = 0
, bottom = 0
, left = 0
}
And then just do
paddingEach { edges | right 5 }
spacing : Basics.Int -> Attribute msg
spacingXY : Basics.Int -> Basics.Int -> Attribute msg
In the majority of cases you'll just need to use spacing
, which will work as intended.
However for some layouts, like textColumn
, you may want to set a different spacing for the x axis compared to the y axis.
spaceEvenly : Attribute msg
Alignment can be used to align an Element
within another Element
.
Element.el [ centerX, alignTop ] (text "I'm centered and aligned top!")
If alignment is set on elements in a layout such as row
, then the element will push the other elements in that direction. Here's an example.
Element.row []
[ Element.el [] Element.none
, Element.el [ alignLeft ] Element.none
, Element.el [ centerX ] Element.none
, Element.el [ alignRight ] Element.none
]
will result in a layout like
|-|-| |-| |-|
Where there are two elements on the left, one in the center, and one on the right.
centerX : Attribute msg
centerY : Attribute msg
alignLeft : Attribute msg
alignRight : Attribute msg
alignTop : Attribute msg
alignBottom : Attribute msg
transparent : Basics.Bool -> Attr decorative msg
Make an element transparent and have it ignore any mouse or touch events, though it will stil take up space.
alpha : Basics.Float -> Attr decorative msg
A capped value between 0.0 and 1.0, where 0.0 is transparent and 1.0 is fully opaque.
Semantically equavalent to html opacity.
pointer : Attribute msg
Set the cursor to be a pointing hand when it's hovering over this element.
moveUp : Basics.Float -> Attr decorative msg
moveDown : Basics.Float -> Attr decorative msg
moveRight : Basics.Float -> Attr decorative msg
moveLeft : Basics.Float -> Attr decorative msg
rotate : Basics.Float -> Attr decorative msg
scale : Basics.Float -> Attr decorative msg
Clip the content if it overflows.
clip : Attribute msg
clipX : Attribute msg
scrollbars : Attribute msg
scrollbarX : Attribute msg
scrollbarY : Attribute msg
layout : List (Attribute msg) -> Element msg -> Html msg
This is your top level node where you can turn Element
into Html
.
layoutWith : { options : List Option } -> List (Attribute msg) -> Element msg -> Html msg
Internal.Model.Option
noStaticStyleSheet : Option
Style elements embeds two StyleSheets, one that is constant, and one that changes dynamically based on styles collected from the elments being rendered.
This option will stop the static/constant stylesheet from rendering.
Make sure to render the constant/static stylesheet at least once on your page!
forceHover : Option
Any hover
styles, aka attributes with mouseOver
in the name, will be always turned on.
This is useful for when you're targeting a platform that has no mouse, such as mobile.
noHover : Option
Disable all mouseOver
styles.
focusStyle : FocusStyle -> Option
{ borderColor : Maybe Color
, backgroundColor : Maybe Color
, shadow : Maybe { color : Color
, offset : ( Basics.Int
, Basics.Int )
, blur : Basics.Int
, size : Basics.Int }
}
link : List (Attribute msg) -> { url : String, label : Element msg } -> Element msg
link []
{ url = "http://fruits.com"
, label = text "A link to my favorite fruit provider."
}
newTabLink : List (Attribute msg) -> { url : String, label : Element msg } -> Element msg
download : List (Attribute msg) -> { url : String, label : Element msg } -> Element msg
A link to download a file.
downloadAs : List (Attribute msg) -> { label : Element msg, filename : String, url : String } -> Element msg
A link to download a file, but you can specify the filename.
image : List (Attribute msg) -> { src : String, description : String } -> Element msg
Both a source and a description are required for images.
The description is used for people using screen readers.
Leaving the description blank will cause the image to be ignored by assistive technology. This can make sense for images that are purely decorative and add no additional information.
So, take a moment to describe your image as you would to someone who has a harder time seeing.
In order to use attributes like Font.color
and Background.color
, you'll need to make some colors!
Internal.Model.Color
rgba : Basics.Float -> Basics.Float -> Basics.Float -> Basics.Float -> Color
rgb : Basics.Float -> Basics.Float -> Basics.Float -> Color
Provide the red, green, and blue channels for the color.
Each channel takes a value between 0 and 1.
rgb255 : Basics.Int -> Basics.Int -> Basics.Int -> Color
Provide the red, green, and blue channels for the color.
Each channel takes a value between 0 and 1.
rgba255 : Basics.Int -> Basics.Int -> Basics.Int -> Basics.Float -> Color
fromRgb : { red : Basics.Float, green : Basics.Float, blue : Basics.Float, alpha : Basics.Float } -> Color
Create a color from an RGB record.
fromRgb255 : { red : Basics.Int, green : Basics.Int, blue : Basics.Int, alpha : Basics.Float } -> Color
toRgb : Color -> { red : Basics.Float, green : Basics.Float, blue : Basics.Float, alpha : Basics.Float }
Deconstruct a Color
into it's rgb channels.
Let's say we want a dropdown menu. Essentially we want to say: put this element below this other element, but don't affect the layout when you do.
Element.row []
[ Element.el
[ Element.below (Element.text "I'm below!")
]
(Element.text "I'm normal!")
]
This will result in
|- I'm normal! -|
I'm below
Where "I'm Below"
doesn't change the size of Element.row
.
This is very useful for things like dropdown menus or tooltips.
above : Element msg -> Attribute msg
below : Element msg -> Attribute msg
onRight : Element msg -> Attribute msg
onLeft : Element msg -> Attribute msg
inFront : Element msg -> Attribute msg
This will place an element in front of another.
Note: If you use this on a layout
element, it will place the element as fixed to the viewport which can be useful for modals and overlays.
behindContent : Element msg -> Attribute msg
This will place an element between the background and the content of an element.
Internal.Model.Attribute decorative msg
This is a special attribute that counts as both a Attribute msg
and a Decoration
.
Internal.Model.Attribute Basics.Never Basics.Never
Only decorations
mouseOver : List Decoration -> Attribute msg
mouseDown : List Decoration -> Attribute msg
focused : List Decoration -> Attribute msg
The main technique for responsiveness is to store window size information in your model.
Install the Browser
package, and set up a subscription for Browser.Events.onResize
.
You'll also need to retrieve the initial window size. You can either run the following task:
Task.perform (\size -> WindowResize { width = size.width, height = size.height }) Window.size
Or pass in window.innerWidth
and window.innerHeight
as flags to your program, which is the preferred way. This requires minor setup on the JS side, but allows you to avoid the state where you don't have window info.
{ class : DeviceClass
, orientation : Orientation
}
classifyDevice : { window | height : Basics.Int, width : Basics.Int } -> Device
Takes in a Window.Size and returns a device profile which can be used for responsiveness.
modular : Basics.Float -> Basics.Float -> Basics.Int -> Basics.Float
When designing it's nice to use a modular scale to set spacial rythms.
scaled =
Scale.modular 16 1.25
A modular scale starts with a number, and multiplies it by a ratio a number of times. Then, when setting font sizes you can use:
Font.size (scaled 1) -- results in 16
Font.size (scaled 2) -- 16 * 1.25 results in 20
Font.size (scaled 4) -- 16 * 1.25 ^ (4 - 1) results in 31.25
We can also provide negative numbers to scale below 16px.
Font.size (scaled -1) -- 16 * 1.25 ^ (-1) results in 12.8
map : (msg -> msg1) -> Element msg -> Element msg1
mapAttribute : (msg -> msg1) -> Attribute msg -> Attribute msg1
html : Html msg -> Element msg
htmlAttribute : Html.Attribute msg -> Attribute msg