How to dynamically change stylesheets globally?

There, was that subject sufficiently obtuse to grab your attention? :slight_smile:

I would like to have several different stylesheets for my application
that may be selected dynamically as the user navigates through my
site. I have seen this done in PHP by passing parameters to each page
visited selecting (in that particular example) a stylesheet with a
larger or smaller font size, or high contrast vs normal contrast
viewing. In the PHP example, the author appended
?fontsize=larger&contrast=high parameter to each link he generated.
Then, on each page, he looked at those parameters and selected the
appropriate stylesheet.

What is the most elegant way to do a similar thing in RoR? Obviously,
the "on each page" part goes into the application layout.

I could follow his lead and simply add the parameters to every link I
generate in my views, but I wonder if there is a way to set a
parameter globally that would automatically get tacked onto any URL's
generated by link_to and friends.

Alternatively, I could store the data used to select the stylesheet in
the session.

Alternatively, I could ask the community what other folks have done
and learn from them.

--wpd

I don't know what the most elegant way is, but you can render
variables and stuff inside of
a layout just like in any other view and call 'render :partial' from
within the layout as well.

You can also do this, inside of the application_controller define:

def self.get_layout
'mylayout'
end

Then in your controllers use this:

layout get_layout

then you can redefine whatever layout you want more dynamically

Thanks... I wasn't aware of self.get_layout. But my issue isn't so
much selecting a layout as it is passing the parameters around to help
me decide which layout (or which stylesheet) I should use.

I can manually append the options to every link_to and form_for,
etc... link I have in my application.

I can store the "which stylesheet should I use" information in the sessions.

There could be some elegant way for RoR to automatically append a
certain set of parameters to any link_to, form_for, etc... generated
link.

Or, there could be a much more obvious way to tackle this problem.

--wpd

Why not just save the user preference in a persistent cookie, so it
continues in effect when the user returns?

Hi Patrick,

There, was that subject sufficiently obtuse to grab your attention? :slight_smile:

I would like to have several different stylesheets for my application
that may be selected dynamically as the user navigates through my
site.

What is the most elegant way to do a similar thing in RoR? Obviously,
the "on each page" part goes into the application layout.

Since Rails allows you to specify the layout to render on a per
controller / per action level the easiest approach might be to have
several layouts. Easy, but it could be a performance-detractor as it
would involve a reload of your JS too.

Another approach would be to make your CSS selectors page-specific. For
example, you could structure your CSS like:

div#login_page {font-size: 16px;}
div#shopping_cart_page{font-size: 12px;}
etc.

And when you render, use controller.controller_name or
controller.action_name, or a combination, to control the html id.

<div id="#{controller.controller_name}_page">

I'm sure there are other, perhaps cleaner approaches.
HTH,
Bill

Right--you typically bring a stylesheet file into your app w/goo like this in the layout:

<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>My Cool App: <%= controller.action_name %></title>
  <%= stylesheet_link_tag 'my_stylesheet' %>
</head>

You can bring as many stylesheets as you want into your layout. So e.g., this will work:

  <%= stylesheet_link_tag 'my_stylesheet' %>
  <%= stylesheet_link_tag 'large_fonts' if cookies[:large_fonts] %>
  <%= stylesheet_link_tag 'high_contrast' if cookies[:high_contrast] %>

Or maybe better:

  <%= stylesheet_link_tag cookies[:font_size] if cookies[:font_size] %>

To let you name the stylesheet file in the cookie, so you can do small/medium/large, say.

Just give the user links to actions that set/unset those cookies & you should be good to go.

HTH,

-Roy

So, what's the difference between session[:large_fonts] and
cookies[:large_fonts]?

I had just about decided to keep the state in the session when I saw
the cookies[:large_fonts] example.

The one issue with keeping the state in the session is that it makes
it much more difficult to bookmark the :large_fonts version of the
page. By appending ?fontsize=large&contrast=normal to every link, the
end user can easily bookmark the page with the correct settings.

I'm guessing from the thundering silence that the need to append a set
of parameters to every generated link doesn't come up that often.
When I've encountered this sort of situation in the past, it has
generally been a good indicator that I'm attempting to make my life
more difficult than it needs to be and that I should rethink my
original problem.

It seems that the RoR way to go is to store the state in the session
or in a cookie. I don't know how important it might prove to be to be
able to bookmark the correctly font sized pages, so I'm going to wait
until somebody asks for it.

Thanks again for the tips.

--wpd

If you use a persistent cookie, any bookmarked page will appear in
whatever setting was selected when your visitor left the site.

You could use all these mechanisms in some order. First, check the URL,
then check the cookie, or the other way round.

Unless your stylesheets are prohibitively large, I suggest you don't
actually switch stylesheets. Instead, set the class of the BODY element
and and add the appropriate classes to your style rules.

Michael

Unless your stylesheets are prohibitively large, I suggest you don't
actually switch stylesheets. Instead, set the class of the BODY element
and and add the appropriate classes to your style rules.

Michael

Hmmm.... I'm inheriting the stylesheets from another project, so I
don't know if that would work or not, but I can look into it.

I am curious, being quite the CSS newbie, how/if it is possible to
change the style rules dynamically. I was under the impression (from
having seen what was done before) that I would have to load different
stylesheets depending on what style (large font, small font, high
contrast, normal contrast, etc...) I wanted my pages displayed.

Are you saying that its possible to globally change the style rules
programatically somehow?

Oh wait a minute... are you suggesting I apply the
"large-font-high-contrast" style to the body element from within the
application template? I see how that could work. What is the benefit
of doing that as compare to the way I've seen it done (on another,
PHP, project) of loading one stylesheet or another at the time the
page is served?

--wpd

> Unless your stylesheets are prohibitively large, I suggest you
> don't actually switch stylesheets. Instead, set the class of the
> BODY element and and add the appropriate classes to your style
> rules.
>
> Michael

Hmmm.... I'm inheriting the stylesheets from another project, so I
don't know if that would work or not, but I can look into it.

I am curious, being quite the CSS newbie, how/if it is possible to
change the style rules dynamically. I was under the impression (from
having seen what was done before) that I would have to load different
stylesheets depending on what style (large font, small font, high
contrast, normal contrast, etc...) I wanted my pages displayed.

Well, that's a way, of course.

Are you saying that its possible to globally change the style rules
programatically somehow?

Yes:

<body class="large-font">
  <div class="whatever">...</div>
</body>

.large-font .whatever { font-size: 200%; }
.whatever { font-size: normal; color: #000; }

Both rules apply to the inner div, thus you get black text, but as the
first rule is more specific, its font-size supersedes the one defined in
the less specific rule.

Oh wait a minute... are you suggesting I apply the
"large-font-high-contrast" style to the body element from within the
application template? I see how that could work.

Not quite. I suggest something like this

<html>
  <head>
    ...
    <script type="text/javascript" src="javascripts/prototype.js" />
    <script type="text/javascript" src="javascripts/switcher.js" />
  </head>
  <body>
    ...
  </body>
</html>

switcher.js: Here the magic goes. Check window.location.href or the
cookie specify any particular style. If they do, change the class of the
BODY element

Element.addClassName(document.body, 'large-font');

What is the
benefit of doing that as compare to the way I've seen it done (on
another, PHP, project) of loading one stylesheet or another at the
time the page is served?

You can apply changes dynamically without a round-trip to the server. If
you currently have pages that are static, but for the style-switching,
you can make them completely static, i.e., they can be served by the web
server without any processing by Rails.

Michael

Are you saying that its possible to globally change the style rules
programatically somehow?

Yes:

<html>
<head>
...
<script type="text/javascript" src="javascripts/prototype.js" />
<script type="text/javascript" src="javascripts/switcher.js" />
</head>
<body>
...
</body>
</html>

switcher.js: Here the magic goes. Check window.location.href or the
cookie specify any particular style. If they do, change the class of the
BODY element

Element.addClassName(document.body, 'large-font');

Oh, I see...

What is the
benefit of doing that as compare to the way I've seen it done (on
another, PHP, project) of loading one stylesheet or another at the
time the page is served?

You can apply changes dynamically without a round-trip to the server. If
you currently have pages that are static, but for the style-switching,
you can make them completely static, i.e., they can be served by the web
server without any processing by Rails.

Nice!
Thanks a lot. I wouldn't have thought of that approach myself, but
it's the right way to do this.

--wpd

If you give the cookie a long lifespan, you won't need the extra formatting preference stuff on the end of the URL.