Discussion:
[elm-discuss] Adding CSS produced by a view function to the document's <head>
Francesco Orsenigo
2017-06-05 10:46:47 UTC
Permalink
I am experimenting with view functions that produce both html content and
the style that should be applied to that markup, as in

myView : Model -> ( Html msg, List CssRule )

So far, I have been rendering the above by creating a <style> tag inside
the <body>: this works great but it is not compliant, so I want to find a
way to insert the styling in the <head>.

I can do this with a port, but where do I execute it?

One solution would be to execute myView inside the program's update cycle,
store the resulting Html in the model, and have a dummy program view
function that just produces the pre-rendered html content stored in the
model, but seems a really weird thing to do and I don't know how it would
impact performance.

update msg model =
let
...

( html, style ) = myView model
in
( { model | renderedView = html }, portUpdateHeaderStyle style )

view model =
model.renderedView


The other solution would be to render everything twice, once in the
program's view to get the html content, and another inside the update cycle
to get the style and execute the port, but executing myView twice doesn't
seem great for performance.

Is there a better way?
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
'Jonas Schürmann' via Elm Discuss
2017-06-05 16:39:16 UTC
Permalink
I am unsure what your goal is, but perhaps elm-css
<https://github.com/rtfeldman/elm-css> can help you. If you want to
generate styles dynamically, it is probably best to use inline styles.
Post by Francesco Orsenigo
I am experimenting with view functions that produce both html content and
the style that should be applied to that markup, as in
myView : Model -> ( Html msg, List CssRule )
So far, I have been rendering the above by creating a <style> tag inside
the <body>: this works great but it is not compliant, so I want to find a
way to insert the styling in the <head>.
I can do this with a port, but where do I execute it?
One solution would be to execute myView inside the program's update
cycle, store the resulting Html in the model, and have a dummy program
view function that just produces the pre-rendered html content stored in
the model, but seems a really weird thing to do and I don't know how it
would impact performance.
update msg model =
let
...
( html, style ) = myView model
in
( { model | renderedView = html }, portUpdateHeaderStyle style )
view model =
model.renderedView
The other solution would be to render everything twice, once in the
program's view to get the html content, and another inside the update cycle
to get the style and execute the port, but executing myView twice doesn't
seem great for performance.
Is there a better way?
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Peter Damoc
2017-06-06 06:47:40 UTC
Permalink
It seams that you have driven yourself into a corner because of a series of
choices that you made with your code architecture.

The best way forward is to refactor you code so that you decouple the style
from the view.
You should be able to get both of them from the model independently.

Once you've done that, you will just need to call the style function with
the new model in `update` and push the output through the port.
On the other side of the port you might want to experiment with a check
against a saved value as to not trigger needless parsing of the mounted
CSS.





On Mon, Jun 5, 2017 at 1:46 PM, Francesco Orsenigo <
Post by Francesco Orsenigo
I am experimenting with view functions that produce both html content and
the style that should be applied to that markup, as in
myView : Model -> ( Html msg, List CssRule )
So far, I have been rendering the above by creating a <style> tag inside
the <body>: this works great but it is not compliant, so I want to find a
way to insert the styling in the <head>.
I can do this with a port, but where do I execute it?
One solution would be to execute myView inside the program's update
cycle, store the resulting Html in the model, and have a dummy program
view function that just produces the pre-rendered html content stored in
the model, but seems a really weird thing to do and I don't know how it
would impact performance.
update msg model =
let
...
( html, style ) = myView model
in
( { model | renderedView = html }, portUpdateHeaderStyle style )
view model =
model.renderedView
The other solution would be to render everything twice, once in the
program's view to get the html content, and another inside the update cycle
to get the style and execute the port, but executing myView twice doesn't
seem great for performance.
Is there a better way?
--
You received this message because you are subscribed to the Google Groups
"Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
There is NO FATE, we are the creators.
blog: http://damoc.ro/
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Francesco Orsenigo
2017-06-06 09:52:08 UTC
Permalink
Coupling view and style is not the problem, it's the *goal*.

Right now, you can't declare CSS in the place where you use it: you need to
create a separate stylesheet with references, use those references in your
view and then put the stylesheet somewhere.
This is not how we write all the rest of our code.
Imagine if every time you declare a function, you also had to put manually
that function in a "list of all functions" and then add a step in your
build pipeline to insert that list in your bundle just so that you can
execute it.
This is what we currently do with CSS, and results in many of the problems
that we have with it.


In private, Peter Damoc kindly suggested that my best shot would be the
view-stored-inside-the-model approach.
Absent any better suggestion, I will pursue that.
Post by Peter Damoc
It seams that you have driven yourself into a corner because of a series
of choices that you made with your code architecture.
The best way forward is to refactor you code so that you decouple the
style from the view.
You should be able to get both of them from the model independently.
Once you've done that, you will just need to call the style function with
the new model in `update` and push the output through the port.
On the other side of the port you might want to experiment with a check
against a saved value as to not trigger needless parsing of the mounted
CSS.
Post by Francesco Orsenigo
I am experimenting with view functions that produce both html content and
the style that should be applied to that markup, as in
myView : Model -> ( Html msg, List CssRule )
So far, I have been rendering the above by creating a <style> tag inside
the <body>: this works great but it is not compliant, so I want to find a
way to insert the styling in the <head>.
I can do this with a port, but where do I execute it?
One solution would be to execute myView inside the program's update
cycle, store the resulting Html in the model, and have a dummy program
view function that just produces the pre-rendered html content stored in
the model, but seems a really weird thing to do and I don't know how it
would impact performance.
update msg model =
let
...
( html, style ) = myView model
in
( { model | renderedView = html }, portUpdateHeaderStyle style )
view model =
model.renderedView
The other solution would be to render everything twice, once in the
program's view to get the html content, and another inside the update cycle
to get the style and execute the port, but executing myView twice
doesn't seem great for performance.
Is there a better way?
--
You received this message because you are subscribed to the Google Groups
"Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
There is NO FATE, we are the creators.
blog: http://damoc.ro/
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Kevin Yank
2017-06-08 11:38:19 UTC
Permalink
The style-elements
<http://package.elm-lang.org/packages/mdgriffith/style-elements/latest>
package introduced at Elm Europe today needs to solve this problem too. For
now, it too is just rendering an invalid <style> tag into the HTML body (see
Element.InternalRender
<https://github.com/mdgriffith/style-elements/blob/3.0.0/src/Element/Internal/Render.elm#L19-L29>
).
Post by Francesco Orsenigo
I am experimenting with view functions that produce both html content and
the style that should be applied to that markup, as in
myView : Model -> ( Html msg, List CssRule )
So far, I have been rendering the above by creating a <style> tag inside
the <body>: this works great but it is not compliant, so I want to find a
way to insert the styling in the <head>.
I can do this with a port, but where do I execute it?
One solution would be to execute myView inside the program's update
cycle, store the resulting Html in the model, and have a dummy program
view function that just produces the pre-rendered html content stored in
the model, but seems a really weird thing to do and I don't know how it
would impact performance.
update msg model =
let
...
( html, style ) = myView model
in
( { model | renderedView = html }, portUpdateHeaderStyle style )
view model =
model.renderedView
The other solution would be to render everything twice, once in the
program's view to get the html content, and another inside the update cycle
to get the style and execute the port, but executing myView twice doesn't
seem great for performance.
Is there a better way?
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Aaron VonderHaar
2017-06-09 04:41:09 UTC
Permalink
I experimented with some ways to have view functions that return both Html
and the necessary styles. I found I could make it work, but I ultimately
abandoned the idea because of a fundamental problem:

view model =
if model.showWidget then
Widget.view model.widgetData
else
text ""

In the code above, depending on the model, you may or may not call
Widget.view. If your view functions are some new type that includes the
necessary styles, then you now have a problem that you will only know about
the styles for views that are currently visible, which means that your
final stylesheet will constantly be changing as the model changes.

What we really want is to get all the styles for all Html that *might*
appear, not for all Html that currently appears. I suspect an API could be
designed that would allow such Html+styles views to be composed in a way
that could provide *all*possible*styles*, but I expect it would probably be
a significant departure from how Html-only views work, so I haven't pursued
that idea further.

At NoRedInk, we have an idea for a way to look at an entry point Elm file
and traverse all of the imported modules, collect all of the top-level
Stylesheets (similar to what the latest elm-test does for Test
definitions), and compile a .css file that we can reference in the <head>
(but this would be done outside of Elm). However, that idea is also
currently in a state of haven't-yet-pursued-further.
Post by Kevin Yank
The style-elements
<http://package.elm-lang.org/packages/mdgriffith/style-elements/latest>
package introduced at Elm Europe today needs to solve this problem too. For
now, it too is just rendering an invalid <style> tag into the HTML body (see
Element.InternalRender
<https://github.com/mdgriffith/style-elements/blob/3.0.0/src/Element/Internal/Render.elm#L19-L29>
).
Post by Francesco Orsenigo
I am experimenting with view functions that produce both html content and
the style that should be applied to that markup, as in
myView : Model -> ( Html msg, List CssRule )
So far, I have been rendering the above by creating a <style> tag inside
the <body>: this works great but it is not compliant, so I want to find a
way to insert the styling in the <head>.
I can do this with a port, but where do I execute it?
One solution would be to execute myView inside the program's update
cycle, store the resulting Html in the model, and have a dummy program
view function that just produces the pre-rendered html content stored in
the model, but seems a really weird thing to do and I don't know how it
would impact performance.
update msg model =
let
...
( html, style ) = myView model
in
( { model | renderedView = html }, portUpdateHeaderStyle style )
view model =
model.renderedView
The other solution would be to render everything twice, once in the
program's view to get the html content, and another inside the update cycle
to get the style and execute the port, but executing myView twice
doesn't seem great for performance.
Is there a better way?
--
You received this message because you are subscribed to the Google Groups
"Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Francesco Orsenigo
2017-06-09 05:05:40 UTC
Permalink
The idea is to keep a set of all the rules added to the header, add only
those that are missing and never remove any.
This will not work for for rules whose definitions are changed dynamically,
but seems like an acceptable trade off.

The ability to calculate all the styles that *might* appear would have the
same limitation.
Post by Aaron VonderHaar
I experimented with some ways to have view functions that return both Html
and the necessary styles. I found I could make it work, but I ultimately
view model =
if model.showWidget then
Widget.view model.widgetData
else
text ""
In the code above, depending on the model, you may or may not call
Widget.view. If your view functions are some new type that includes the
necessary styles, then you now have a problem that you will only know about
the styles for views that are currently visible, which means that your
final stylesheet will constantly be changing as the model changes.
What we really want is to get all the styles for all Html that *might*
appear, not for all Html that currently appears. I suspect an API could be
designed that would allow such Html+styles views to be composed in a way
that could provide *all*possible*styles*, but I expect it would probably be
a significant departure from how Html-only views work, so I haven't pursued
that idea further.
At NoRedInk, we have an idea for a way to look at an entry point Elm file
and traverse all of the imported modules, collect all of the top-level
Stylesheets (similar to what the latest elm-test does for Test
definitions), and compile a .css file that we can reference in the <head>
(but this would be done outside of Elm). However, that idea is also
currently in a state of haven't-yet-pursued-further.
Post by Kevin Yank
The style-elements
<http://package.elm-lang.org/packages/mdgriffith/style-elements/latest>
package introduced at Elm Europe today needs to solve this problem too. For
now, it too is just rendering an invalid <style> tag into the HTML body (see
Element.InternalRender
<https://github.com/mdgriffith/style-elements/blob/3.0.0/src/Element/Internal/Render.elm#L19-L29>
).
Post by Francesco Orsenigo
I am experimenting with view functions that produce both html content
and the style that should be applied to that markup, as in
myView : Model -> ( Html msg, List CssRule )
So far, I have been rendering the above by creating a <style> tag inside
the <body>: this works great but it is not compliant, so I want to find a
way to insert the styling in the <head>.
I can do this with a port, but where do I execute it?
One solution would be to execute myView inside the program's update
cycle, store the resulting Html in the model, and have a dummy program
view function that just produces the pre-rendered html content stored
in the model, but seems a really weird thing to do and I don't know how it
would impact performance.
update msg model =
let
...
( html, style ) = myView model
in
( { model | renderedView = html }, portUpdateHeaderStyle style )
view model =
model.renderedView
The other solution would be to render everything twice, once in the
program's view to get the html content, and another inside the update cycle
to get the style and execute the port, but executing myView twice
doesn't seem great for performance.
Is there a better way?
--
You received this message because you are subscribed to the Google Groups
"Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...