-
-
Save mdgriffith/c2abad030640dd20e0aa3bc3519211e4 to your computer and use it in GitHub Desktop.
{- A sketch for a potential responsive API for elm-ui | |
First, the API. Following will be some example code! | |
-} | |
-- defining a set of global window-width breakpoints | |
breakpoints : label -> List (Breakpoint label) -> Breakpoints label | |
breakAt : Int -> label -> Breakpoint label | |
{-| Define a layout that is a row when the page in in the specified breakpoints. | |
Otherwise, it'll render as a `column`. | |
-} | |
rowWhen : Breakpoints label -> List label -> List (Attribute msg) -> List (Element msg) | |
-- an exact value | |
value : Int -> Value | |
-- a value that will scale from one value to another depending | |
-- on it's position in the current window-width range | |
-- See fluid typography example below | |
fluid : Int -> Int -> Value | |
-- Properties that can vary based on the given window size | |
fontSize : Breakpoints label -> (label -> Value) -> Attribute msg | |
padding : Breakpoints label -> (label -> Value) -> Attribute msg | |
height : Breakpoints label -> (label -> Value) -> Attribute msg | |
heightMin : Breakpoints label -> (label -> Value) -> Attribute msg | |
heightMax : Breakpoints label -> (label -> Value) -> Attribute msg | |
width : Breakpoints label -> (label -> Value) -> Attribute msg | |
widthMin : Breakpoints label -> (label -> Value) -> Attribute msg | |
widthMax : Breakpoints label -> (label -> Value) -> Attribute msg | |
{- EXAMPLES -} | |
{-| | |
Defined statically. Needs to be provided to Ui.layout so it can render media queries. | |
Also needs to be provided to all elements that need responsiveness. But since it's top-level, no need to thread it through every view fn. | |
-} | |
breakpoints : Ui.Responsive.Breakpoints Breakpoints | |
breakpoints = | |
Ui.Responsive.breakpoints ExtraLarge | |
[ Ui.Responsive.breakAt 2400 Large | |
, Ui.Responsive.breakAt 1400 Medium | |
, Ui.Responsive.breakAt 800 Small | |
] | |
el | |
[ Ui.Responsive.visible breakpoints | |
[ Medium ] | |
] | |
(text "only visible at medium") | |
el | |
[ Ui.Responsive.fontSize breakpoints | |
(\breakpoint -> | |
case breakpoint of | |
ExtraLarge -> | |
Ui.Responsive.value 35 | |
Large -> | |
Ui.Responsive.value 35 | |
Medium -> | |
-- scales from 16 to 35 when the window is in the `Medium` range | |
Ui.Responsive.fluid 16 35 | |
Small -> | |
Ui.Responsive.value 16 | |
) | |
] | |
(text "Fluid typography") | |
-- padding | |
el | |
[ Ui.Responsive.padding breakpoints | |
(\breakpoint -> | |
case breakpoint of | |
ExtraLarge -> | |
Ui.Responsive.value 35 | |
Large -> | |
Ui.Responsive.value 35 | |
Medium -> | |
-- scales from 16 to 35 when the window is in the `Medium` range | |
Ui.Responsive.fluid 16 35 | |
Small -> | |
Ui.Responsive.value 16 | |
) | |
] | |
(text "Responsive padding!") |
Yeah, it is necessary unless you want to add a breakpoint type variable to every Element and Attribute
Wouldn't that be preferable to having it passed into every responsive value?
or ditch the custom type there
I suppose for the case of composition, having it as a custom type could be tricky. There could be some sort of mapBreakpoint
, but how that would work in practice...
On the other hand providing some sort of fixed vocabulary seems really tricky.
Alternatively this would be much easier if elm-ui did it's own layout calculations, which is something I'd love to explore! However it'd be quite involved to introduce.
At work we have a custom UI system that's kind of inspired by elm-ui and it does this (in our case only for widths since in practice branching on height isn't very common and it's harder to get right). We basically get a subscription for the window height, then thread the height throughout the layout tree, computing paddings, widths and spacings as appropriate. The width is always a Maybe, since it's impractical to measure text, so if the width depends on text content, we can't provide it. In practice this is actually rare (although our rows and columns default to the equivalent of width fill
, so perhaps that makes it easier); furthermore it's even rarer for a particular element to change between Just x
and Nothing
- typically the width is either knowable or not.
Also the calculations are accurate with some error, which seems to be due to the fact that different OSs reserve space for scrollbars in different ways, which ocassionally eats up a few pixels of space. I haven't yet figured out a way to reliably take this into account, but I suspect that for most responsive use cases a ±5px accuracy wouldn't be too much of a problem.
We actually don't use this for responsive design at the moment (we don't really support mobile browsers at all... ¯_(ツ)_/¯ ), but use it for composable SVG drawing that needs to be responsive. And it works really well.
Wouldn't that be preferable to having it passed into every responsive value?
I personally don't think so - the additional cognitive complexity for type signatures would be pretty big because this type is everywhere and would need to be explained to anyone starting to use the library. Easy for people who are very familiar with Elm, but I want elm-ui
to be a gateway project, so I'm maybe more wary of cognitive overhead than usual.
I don't think mapping breakpoints would actually be useful because you're never reading them beyond essentially doing a ==
Also not many elements actually need to be explicitly responsive like this. It's usually a handful compared to the vast majority of times when width fill
is plenty of responsiveness.
Also, ideally you'd have only a few prepackaged responsive properties like this. e.g. Theme.padding.responsive.medium
.
At work we have a custom UI system that's kind of inspired by elm-ui and it does this
Cool! Yeah, measuring text is the main blocker. I have a few ideas on how to do that-happy to share once I'm focusing on that directly.
Yeah, it is necessary unless you want to add a
breakpoint
type variable to everyElement
andAttribute
, or ditch the custom type there.Ultimately, yeah, that's an approach I'd like to move towards. If I'm following you, In CSS land, these are called
container queries
and a lot of people want them! But they don't exist in CSS yet.Alternatively this would be much easier if elm-ui did it's own layout calculations, which is something I'd love to explore! However it'd be quite involved to introduce.