miyamoen / elm-origami / Origami

Define CSS in Elm.

input
    [ type_ "checkbox"
    , name "toggle-all"
    , checked allCompleted
    , onClick (CheckAll (not allCompleted))
    , css
        [ property "position" "absolute"
        , property "top" "-55px"
        , property "left" "-12px"
        , property "width" "60px"
        , property "height" "34px"
        , property "text-align" "center"
        , property "border" "none" -- Mobile Safari
        , with "::before"
            [ property "content" <| qt "❯"
            , property "font-size" "22px"
            , property "color" "#e6e6e6"
            , property "padding" "10px 27px 10px 27px"
            ]
        , with ":checked::before"
            [ property "color" "#737373" ]
        , -- Hack to remove background from Mobile Safari.
          -- Can't use it globally since it destroys checkboxes in Firefox
          withMedia "screen and (-webkit-min-device-pixel-ratio:0)"
            [ property "background" "none"
            , property "-webkit-transform" "rotate(90deg)"
            , property "transform" "rotate(90deg)"
            , property "-webkit-appearance" "none"
            , property "appearance" "none"
            ]
        ]
    ]
    []

How to Use

css and basic functions

label
    [ css
        [ property "white-space" "pre-line"
        , property "word-break" "break-all"
        , property "padding" "15px 60px 15px 15px"
        , property "margin-left" "45px"
        , property "display" "block"
        , property "line-height" "1.2"
        , property "transition" "color 0.4s"
        , if todo.completed then
            batch
                [ property "color" "#d9d9d9"
                , property "text-decoration" "line-through"
                ]

          else
            noStyle
        ]
    , onDoubleClick (EditingEntry todo.id True)
    ]
    [ text todo.description ]


type alias Style =
Css.Style.Style

css関数に渡す型です

property : String -> String -> Style

Create a property style.

css [ property "-webkit-font-smoothing" "none" ]

...outputs

-webkit-font-smoothing: none;

batch : List Style -> Style

Create a batched style.

underlineOnHover =
    batch
        [ textDecoration none
        , hover
            [ textDecoration underline ]
        ]

css
    [ color (rgb 128 64 32)
    , underlineOnHover
    ]

...has the same result as:

css
    [ color (rgb 128 64 32)
    , textDecoration none
    , hover
        [ textDecoration underline ]
    ]

noStyle : Style

Create an empty style.

if predicate then
    property "key" "value"

else
    noStyle

Create a Style Tag

._1e7eb6f2 {
    white-space: pre-line;
    word-break: break-all;
    padding: 15px 60px 15px 15px;
    margin-left: 45px;
    display: block;
    line-height: 1.2;
    transition: color 0.4s;
}

label側は以下のように記述されたのと同様になります

label
    [ class "_1e7eb6f2"
    , onDoubleClick (EditingEntry todo.id True)
    ]
    [ text todo.description ]

Nested Style

button
    [ css
        [ property "display" "none"
        , property "position" "absolute"
        , property "top" "0"
        , property "right" "10px"
        , property "bottom" "0"
        , property "width" "40px"
        , property "height" "40px"
        , property "margin" "auto 0"
        , property "font-size" "30px"
        , property "color" "#cc9a9a"
        , property "margin-bottom" "11px"
        , property "transition" "color 0.2s ease-out"
        , with ":hover" [ property "color" "#af5b5e" ]
        , with "::after" [ property "content" "'×'" ]
        ]
    , onClick (Delete todo.id)
    ]
    []

これは以下のように書き出されます

._b75a75af {
    display: none;
    position: absolute;
    top: 0;
    right: 10px;
    bottom: 0;
    width: 40px;
    height: 40px;
    margin: auto 0;
    font-size: 30px;
    color: #cc9a9a;
    margin-bottom: 11px;
    transition: color 0.2s ease-out;
}

_b75a75af:hover {
    color: #af5b5e;
}

._b75a75af::after {
    content: '×';
}

with : String -> List Style -> Style

Nest CSS with a selector. 生成されたhash値に続けて付加されるのでspace sensitiveです。空白が挟まった場合子孫セレクタと解釈されえます。

css
    [ with ".classname" [ property "key" "value" ] ]

...outputs

_xxx.classname {
    key: value;
}

Media Query

ul
    [ css
        [ property "margin" "0"
        , property "padding" "0"
        , property "list-style" "none"
        , property "position" "absolute"
        , property "right" "0"
        , property "left" "0"
        , withMedia "(max-width: 430px)" [ property "bottom" "10px" ]
        ]
    ] [..]

...outputs

._a85c725b {
    margin: 0;
    padding: 0;
    list-style: none;
    position: absolute;
    right: 0;
    left: 0;
}



<a name="//apple_ref/cpp/Function/withMedia" class="dashAnchor"></a>

<div style="padding: 0px; margin: 0px; height: 1px; background-color: rgb(216, 221, 225);"></div>

<strong> <a class="mono" name="withMedia" href="#withMedia">withMedia</a> <span class="grey"> :</span> </strong><span class="mono">String <span class="grey">-&gt;</span> List Style <span class="grey">-&gt;</span> Style</span>

 Nest CSS with a media query.

    css [ withMedia "screen" [ property "key" "value" ] ]

...outputs

```css
@media screen {
    _xxx {
        key: value;
    }
}

note: Media Queryを複数回入れ子にすることはできません

css
    [ withMedia "screen"
        [ property "key" "value"
        , withMedia "not nestable" [ property "key" "value" ]
        , with ".nestable" [ property "key" "value" ]
        ]
    ]

..outputs

@media screen {
    _xxx {
        key: value;
    }
}

@media screen {
    _xxx.nestable {
        key: value;
    }
}

withCustom : String -> String -> List Style -> Style

Nest CSS with a selector and a media query.

css
    [ withCustom "(max-width: 430px)" ".classname" <|
        [ property "key" "value" ]
    ]

...outputs

@media (max-width: 430px) {
    _xxx.classname {
        key: value;
    }
}

Animation Style

let
    loadingStyle =
        batch
            [ property "content" <| qt ""
            , property "position" "absolute"
            , property "width" "60%"
            , property "height" "60%"
            , property "border-radius" "100%"
            , property "border" "calc(30px / 10) solid transparent"
            , animation
                [ ( "from", [ property "transform" "rotate(0deg)" ] )
                , ( "to", [ property "transform" "rotate(360deg)" ] )
                ]
            , property "animation-duration" "1s"
            , property "animation-iteration-count" "infinite"
            ]
in
batch
    [ property "border-radius" "50px"
    , property "width" "50px"

    -- [Copyright (c) 2019 Epicmax LLC](https://epic-spinners.epicmax.co/)
    , with "::after" [ loadingStyle, property "border-top-color" "#ffe9ef" ]
    , with "::before"
        [ loadingStyle
        , property "border-bottom-color" "#ffe9ef"
        , property "animation-direction" "alternate"
        ]
    ]

animation : List ( String, List Style ) -> Style

Define a animation.

css
    [ animation
        [ ( "from", [ property "key" "value" ] )
        , ( "20%, 80%", [ property "key" "value" ] )
        , ( "to", [ property "key" "value" ] )
        ]
    ]

...outputs

.hashed-class-name {
    animation-name: hashed-animation-name;
}

@keyframes hashed-animation-name {
    from {
        key: value;
    }

    20%, 80% {
        key: value;
    }

    to {
        key: value;
    }
}

note: 引数のList Stylewithなどの入れ子とanimation自身を使った場合無視されます。

Helper

qt : String -> String

For use with font-family and so on.

property "font-family" (qt "Gill Sans Extrabold")

Advanced topics

CSSの重複除去は行われる?

div []
    [ div [ css [ property "key" "value" ] ] []
    , div [ css [ property "key" "value" ] ] []
    , div [] [ div [ css [ property "key" "value" ] ] [] ]
    ]

...outputs only

._xxx {
    key: value;
}

css関数を複数使ったらどうなる?

div
    [ css [ property "display" "none" ]
    , css [ property "display" "block" ]
    ]
    [ text "表示されますか?" ]

...outputs

._xxx {
    display: none;
    display: block;
}

クラス名がハッシュ値で読みにくい

cssWithClass : String -> List Style -> Attribute msg
cssWithClass classname styles =
    batchAttributes [ Attributes.class classname, css [ with ("." ++ classname) styles ] ]