Style the tabs directly

tl;dr; Use a class for each variant instead of overriding one variant.

Before, we relied on CSS specificity in an unclear way.
The `Focused` class was applying properly because it was ordered later
than the `Tab` class in the stylesheet.
The ordering that is important is the ordering in `styles` value.
Since `elm-css` generates the stylesheet in the order of the lists,
the `Focused` rule would be generated after the `Tab` rule.
Meaning the `Focused` rule would take precedence over the `Tab` rule
if an element had both classes as it was defined later in the stylesheet.

There are some concerns with this approach:
1. It's not readily apparent that the ordering in `styles` is important.
    It is pretty easy to change the ordering of the list
    and have it break the styling.
2. We rely on `elm-css` to generate the stylesheet in a specific order.
    If it changes the order of rules it generates,
    we're almost surely going to break the styling.
3. Altering styles for tabs that are not focused is even less intuitive.
    Since the specificity is the same,
    you might not know why a given rule applies (or doesn't apply).

Rather, we can eschew the specificity/precedence issues
by applying a different class to each tab.
The stuff that is the same can stay on the `Tab` class,
and the stuff that differs can be on different classes.
This commit is contained in:
Hardy Jones 2018-03-26 19:52:33 -07:00
parent dd164e3e57
commit 7ebb488786
No known key found for this signature in database
GPG Key ID: 53FEB62789F62A82

View File

@ -0,0 +1,113 @@
module Nri.Ui.SegmentedControl.V2 exposing (Config, Option, styles, view)
{-|
@docs Config, Option, styles, view
-}
import Accessibility exposing (..)
import Accessibility.Role as Role
import Css exposing (..)
import Css.Foreign exposing (Snippet, adjacentSiblings, children, class, descendants, each, everything, media, selector, withClass)
import Html
import Html.Attributes
import Html.Events
import Nri.Ui.Colors.Extra exposing (withAlpha)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.CssFlexBoxWithVendorPrefix as FlexBox
import Nri.Ui.Fonts.V1
import Nri.Ui.Styles.V1
{-| -}
type alias Config a msg =
{ onClick : a -> msg
, options : List (Option a)
, selected : a
}
{-| -}
type alias Option a =
{ value : a
, label : String
, id : String
}
{-| -}
view : Config a msg -> Html.Html msg
view config =
config.options
|> List.map
(\option ->
Html.div
[ Html.Attributes.id option.id
, Role.tab
, Html.Events.onClick (config.onClick option.value)
, styles.class
[ Tab
, focusedClass option.value config.selected
]
]
[ Html.text option.label
]
)
|> div [ Role.tabList, styles.class [ SegmentedControl ] ]
focusedClass : a -> a -> CssClass
focusedClass value selected =
if value == selected then
Focused
else
Unfocused
type CssClass
= SegmentedControl
| Tab
| Focused
| Unfocused
{-| -}
styles : Nri.Ui.Styles.V1.Styles Never CssClass msg
styles =
Nri.Ui.Styles.V1.styles "Nri-Ui-SegmentedControl-V2-"
[ Css.Foreign.class SegmentedControl
[ FlexBox.displayFlex
, cursor pointer
]
, Css.Foreign.class Tab
[ padding2 (px 6) (px 20)
, height (px 45)
, Nri.Ui.Fonts.V1.baseFont
, fontSize (px 15)
, fontWeight bold
, lineHeight (px 30)
, firstChild
[ borderTopLeftRadius (px 8)
, borderBottomLeftRadius (px 8)
, borderLeft3 (px 1) solid Colors.azure
]
, lastChild
[ borderTopRightRadius (px 8)
, borderBottomRightRadius (px 8)
]
, border3 (px 1) solid Colors.azure
, borderLeft (px 0)
, boxSizing borderBox
]
, Css.Foreign.class Focused
[ color Colors.gray20
, backgroundColor Colors.glacier
, boxShadow5 inset zero (px 3) zero (withAlpha 0.2 Colors.gray20)
, borderBottom3 (px 1) solid Colors.azure
]
, Css.Foreign.class Unfocused
[ borderBottom3 (px 3) solid Colors.azure
, color Colors.azure
]
]