elm-css icon indicating copy to clipboard operation
elm-css copied to clipboard

Support CSS Grid Layout

Open Enkidatron opened this issue 7 years ago • 12 comments

I would like to start a discussion around supporting the CSS Grid features. CSS Grid is usable on over 50% of devices. Grid is also a powerful enough feature that it is the future of CSS. However, it is rather complicated, from an API perspective. Here are the properties that would need to be supported:

  • display: grid
  • grid-template-columns
  • grid-template-rows
  • grid-template-areas
  • grid-template
  • grid-auto-columns
  • grid-auto-rows
  • grid-auto-flow
  • grid
  • grid-row-start
  • grid-column-start
  • grid-row-end
  • grid-column-end
  • grid-row
  • grid-column
  • grid-area
  • grid-row-gap
  • grid-column-gap
  • grid-gap

It also has the repeat, minmax, and fit-content css functions.

I believe that also supporting justify-items, justify-content, align-items, and align-content would also be good to do, but I do think that an initial implementation could leave off some of the properties that are just shorthand properties, like grid-template.

Some of these are fairly straightforward, like grid-gap and friends, as they take existing types (ExplicitLength units, I believe), and have a limited number of input formats.

Things get more complicated with properties like grid-template-columns, which has a variety of formats and limitations. Formal spec can be found here. It is further complicated by the new repeat and minmax functions essentially have multiple type signatures, depending on what else is around them.

I took a look at this over the weekend, so I already have a few ideas, but I will put them in a comment, so that they are not mixed up with the problem description.

Enkidatron avatar May 22 '17 18:05 Enkidatron

Over the weekend, I started looking at grid-template-columns, and identified a number of types that I think would be useful, along with a few options for function type signatures.

Pulling types out of the formal spec, it looked to me that all or some of the following were useful types to keep track of (the names are copied from the formal spec, so it should be possible to follow along):

  • TrackListOrAutoTrackListOrNone (This is what we must ultimately turn into. It might not be needed, depending on how the property functions work)
  • TrackSizeOrTrackRepeatOrLineNames
  • TrackSizeOrTrackRepeat
  • LineNames
  • TrackSizeOrLineNames
  • TrackSize
  • InflexibleBreadth
  • TrackBreadth
  • AutoRepeat
  • AutoFillOrAutoFit
  • FixedSizeOrFixedRepeatOrLineNames
  • FixedSizeOrFixedRepeat
  • FixedSizeOrLineNames
  • FixedSize

This seems like a lot of types, but I have use cases for all/most of them. Most of them are related in a tree structure, so that anything that is a TrackBreadth is automatically also a TrackSize, TrackSizeOrLineName, TrackSizeOrTrackRepeat, and TrackSizeOrTrackRepeatOrLineName.

My first draft had a single gridTemplateColumns : TrackListOrAutoTrackListOrNone compatible -> Style function, and then three functions that create TrackListOrAutoTrackListOrNone values (aside from none itself), but that could and probably should be swapped around to having three gridTemplateColumns functions, which would allow us to get rid of the top level TrackListOrAutoTrackListOrNone compatible type. However, if you did want to explicitly set this property to "none", you would either need a custom function, or perhaps to just pass one of the functions an empty list, which is not as obvious as passing none.

Here are the three property constructor functions I have so far:

  • trackList1 : List (TrackListOrTrackRepeat c1) -> TrackListOrAutoTrackListOrNone c2
    • This simply does not accept line names, because a definition with only line names is invalid, so we need to make sure that we have at least one non-name entry. If the list is empty, we would go with "none". TrackListOrTrackRepeat c includes things like px 100 and pct 60 and repeat 3 [px 30, fr 1].
  • trackList2 : LineNames c1 -> TrackSizeOrTrackRepeat c2 -> List (TrackSizeOrTrackRepeatOrLineNames c3) -> TrackListOrAutoTrackListOrNone c4
    • This version allows you to put line names in, while still requiring at least one non-name entry.
    • This assumes that any adjacent LineName entries in the list get mushed together, because they are not allowed by the spec to be next to each other. Each line name group gets written as "[linename1 linename2]", so it should be as easy as scanning for and removing any instances of "] [".
    • The LineNames type can be empty, so that beginning LineNames c1 is kind of optional. Maybe another version of this function should be created that leaves off that first name group.
    • The main alternative I had to having a list of entriesOrNames interspersed like that was to have a list of (LineNames, TrackSizeOrTrackRepeat) tuples, but I found it to be very cumbersome to use.
  • autoTrackList : List (FixedSizeOrFixedRepeatOrLineNames c1) -> AutoRepeat c2 -> List (FixedSizeOrFixedRepeatOrLineNames c3) -> TrackListOrAutoTrackListOrNone c4
    • This is the version that allows you to use the auto-repeats, which is any time you use the repeat css function with the auto-fit or auto-fill keywords. Using the auto-repeat limits what you can do elsewhere, which is why there is the giant split between the FixedSize types and the TrackSize types.
    • Like the previous function, this assumes that any adjacent LineNames will be put together into a single group of square brackets.

There are a few CSS functions that we should mimic, which are allowed on both sides of the TrackSize/FixedSize split, so I have multiple versions of them that resolve to different things. Here are the functions for those:

  • repeat : Int -> List (TrackSize c1) -> TrackSizeOrTrackRepeat c2
    • This is basically like trackList1, except you cannot put a repeat inside of another repeat.
    • It is currently taking an Int, but maybe it should take a Number compatible (Css.int 3 instead of 3)?
  • repeat2 : Int -> LineNames c1 -> TrackSize c2 -> List (TrackSizeOrLineNames c3) -> TrackSizeOrTrackRepeat c4
    • This is like trackList2, aside from not being able to put a repeat inside it.
  • fixedRepeat1 : Int -> List (FixedSize c1) -> FixedSizeOrFixedRepeat c2
    • Like repeat, but over on the FixedSize side of the split.
  • fixedRepeat2 : Int -> LineNames c1 -> FixedSize c2 -> List (FixedSizeOrLineNames c3) -> TrackSizeOrFixedRepeat c4
    • Again, maybe there should be a version that leaves the first LineNames c1 off of it.
  • autoRepeat1 and autoRepeat2 are various shortenings of autoRepeat3:
  • autoRepeat3 : AutoFillOrAutoFit c1 -> LineNames c2 -> FixedSize c3 -> List (FixedSizeOrLineNames c4) -> AutoRepeat c5
  • minmax1 : InflexibleBreadth c1 -> TrackBreadth c2 -> TrackSize c3
  • minmax2 : InflexibleBreadth c1 -> FixedBreadth c2 -> FixedSize c3
  • minmax3 : FixedBreadth c1 -> TrackBreadth c2 -> FixedSize c3
    • minmax2 and minmax3 are strictly less powerful than minmax1, but that's what is necessary for them to work with the auto-repeats.
    • I have concerns here, because there is very little discoverability as to when you can use which one of these.
    • Perhaps FixedSize could be made a sub-set of TrackSize, and the minmax function would return an object that lists itself as compatible with everything it can be based on the input? But I don't know a way to encode that in the type system, because the return type would have to be listed as FixedSize compatible, not TrackSize compatible, which would seem to prevent it from being used over in those safe areas, even if it actually would work based on what you gave it.
  • fitContent : LengthOrPercent c1 -> TrackSize c2
    • I think that LengthOrPercent c1 is actually ExplicitLength units, but I didn't look closely enough to confirm it.

Generating line names is simple:

  • lineNames : List String -> LineNames c
  • lineName : String -> LineNames c

A few new values generators need to be created:

  • fr : Float -> Fr
    • `fr = flexLengthConverter Fr "fr"
    • where flexLengthConverter is basically the same as lengthConverter, but not compatible with nearly as many things. It gives back a FlexLength compatible, which is a new type.

After those, the types just need to be added to the existing value generators:

  • auto -> most of the TrackSize types
  • minContent and maxContent -> Most of the TrackSize types
  • LengthOrPercent c (again, I think this is actually ExplicitLength) -> basically all of the types
  • global values : add TrackListOrAutoTrackListOrNone, if it still exists at the end of this.

using my first draft API, the examples from the MDN page are written as follows:

  • gridTemplateColumns none

  • gridTemplateColumns <| tracklist1 [px 100, fr 1]

  • gridTemplateColumns <| trackList2 (lineNames ["linename"]) (px 100) []

  • gridTemplateColumns <| trackList2 (lineNames ["linename"]) (px 100) [lineNames ["linename2","linename3"]]

  • gridTemplateColumns <| minmax1 (px 100) (fr 1)

  • gridTemplateColumns <| fitContent (pct 40)

  • gridTemplateColumns <| repeat 3 [px 200]

  • gridTemplateColumns <| autoTrackList [px 200] (autoRepeat1 autoFill [ px 100]) [px 300]

  • gridTemplateColumns <| autoTrackList [minmax3 (px 100) maxContent] (autoRepeat1 autoFill (px 200)) [pct 20] ( gridTemplateColumns <| autoTrackList [lineName "linename1", px 100, lineName "linename2"] (autoRepeat3 autoFit (lineNames ["linename3","linename4"]) (px 300) []) [px 100]

  • gridTemplateColumns <| autoTrackList [lineNames ["linename1","linename2"] ,px 100] (autoRepeat3 autoFit (lineName "linename1") (px 300) []] [lineName "linename3"]

All of this is just for the grid-template-columns property, but I do think that it encompasses most of the complexity required for the entire Grid API, particularly if the more complicated shorthand functions are left off.

Thanks for your attention. This was rather long, and I appreciate your attention in getting through it. I am willing to do the work to put together the PR once the community has settled on an API for it.

Any and all feedback is appreciated.

Enkidatron avatar May 22 '17 21:05 Enkidatron

I don't have the Elm chops to comment on implementation, but agree that CSS Grid is important!

frou avatar Sep 10 '17 05:09 frou

@Enkidatron Is there any update on getting support for grids?

AntouanK avatar Feb 02 '18 18:02 AntouanK

I would also love to have grid support in elm-css. using property does work but the added type safety of elm-css would be much appreciated

peacememories avatar Apr 16 '18 22:04 peacememories

+1 - this would be great.

janwirth avatar Nov 27 '18 10:11 janwirth

Would really like to contribute to this (as I am in desperate need for a grid system in elm/css). How can I contribute?

hylkesebus avatar Apr 15 '20 19:04 hylkesebus

👋 I guess this is never going to be a thing, right?

bigardone avatar Jan 28 '21 09:01 bigardone

Sadly I haven’t had time to get into it, nor do I have a clue how to contribute yet. I’m currently just using a work-around with Elm’s default style function.

hylkesebus avatar Jan 28 '21 09:01 hylkesebus

No worries. I was asking because I wonder if PRs are still welcome. I see a lot of PRs which were open a long time ago and never merged. Implementing this looks like a lot of work to eventually not being merged :(

bigardone avatar Jan 28 '21 09:01 bigardone

A fair concern, I’m still very inexperienced with GitHub, so I wouldn’t know if suggesting a fork is a realistic option but I guess that if this project is somewhat abandoned we could do that? Again, I do not know what “normal” procedure is in such cases!

hylkesebus avatar Jan 28 '21 09:01 hylkesebus

I've found this https://package.elm-lang.org/packages/clojj/elm-css-grid/latest/SimpleGrid, which apparently implements the grid layout with elm-css 🎉

bigardone avatar Jan 28 '21 10:01 bigardone

Sadly that is not the kind of implementation I'm looking for, thanks nonetheless, much appreciate it! :)

hylkesebus avatar Jan 28 '21 12:01 hylkesebus