Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject user settings in non-FXL HTML resources #101

Closed
HadrienGardeur opened this issue Jun 13, 2018 · 9 comments
Closed

Inject user settings in non-FXL HTML resources #101

HadrienGardeur opened this issue Jun 13, 2018 · 9 comments
Labels
enhancement New feature or request

Comments

@HadrienGardeur
Copy link
Contributor

HadrienGardeur commented Jun 13, 2018

Based on the work on a new user settings model, we'll also need to start injecting them as well at a streamer level.

As far as I can tell, this will only be the case for non-FXL resources, which means that before anything is injected we'll need to check:

  • if a specific resource is marked as FXL (in properties)
  • if the overall publication is marked as FXL

If we don't have a link-level helper for that, this would be a good time to implement something (for instance link.fxl? that returns a boolean).

Once we've established that a resource is HTML and non-FXL, we can then proceed to injecting these user settings.

I'll let @JayPanoz chime in on the best way of injecting these CSS Custom Properties but basically:

  • we'll use name for the CSS property name
  • and we'll use value for the CSS property value

Serializing this to CSS Custom Properties should be fairly trivial.

@HadrienGardeur HadrienGardeur added the enhancement New feature or request label Jun 13, 2018
@JayPanoz
Copy link

JayPanoz commented Jun 13, 2018

So to sum things up:

  • all values are strings (even integers e.g. page margins);
  • all user settings have a property and a value e.g. --RS__userSettingProperty: userSettingValue; so that’s name + value in the model;
  • this pair is separated by a colon (:), pairs are separated by a semicolon (;);
  • user settings should be appended in a style attribute on html – if the attribute is already present, user settings should be added to it, not override the existing value;
  • some settings need what we call flags i.e. advanced settings like word-spacing, cf. “required flag” in the ReadiumCSS docs.

And that’s pretty much it. Adding settings to the html element is basically our best option there as rendering engines are optimized for that use case – it’s an easy performance trick.

A question though: currently all user settings are persistent across ebooks. Do we want to keep it that way or reserve persistence to some settings – in which case, we could have a mapping to the user overrides classification.

@HadrienGardeur
Copy link
Contributor Author

some settings need what we call flags i.e. advanced settings like word-spacing, cf. “required flag” in the ReadiumCSS docs.

From a model perspective, these will separate values. At an "app" level (the test app in our case), we could certainly be a little smarter though and tie several values together.

@JayPanoz
Copy link

JayPanoz commented Jun 13, 2018

Yeah definitely. Perhaps the most important is that the flag for advanced settings is shared so if one advanced user setting is already using it, you’re good to go for others requiring it.

[edit] Clarify which flag is shared.

@clementbmn
Copy link

clementbmn commented Jun 20, 2018

If UserProperties.css= "color: blue;"
Arent <link rel="stylesheet" type="text/css" href="UserProperties.css"/>
and <html style="color: blue;"/> basically the same thing ?
Simple question

@JayPanoz
Copy link

JayPanoz commented Jun 20, 2018

Hmm,

<link rel="stylesheet" type="text/css" href="UserProperties.css"/>

and

<style type="text/css">
  /* same content as stylesheet */
</style>

are indeed more or less the same thing but we’re not doing that in ReadiumCSS, although that’s an option should an implementer use another CSS library.

Currently, what we’re doing is something like:

<html style="--USER__fontOverride: readium-font-on; --USER__fontFamily: 'Helvetica'; --USER__advancedSettings: readium-advanced-on; --USER__fontSize: 200%;">

So that’s

<html style="name: value; name: value; name: value; name: value">

in the new user settings model. ReadiumCSS is indeed using the style names and/or values to apply the styles so if they’re not present in <html>, user settings won’t work.

The alternative being injecting the entire styles for each setting via <link> or <style> but right now we get a performance boost with inline styles on <html>.

@JayPanoz
Copy link

Took some extra look at the model on the r2 repo and just recording my thoughts but…

If developers want to use <link> or <style> i.e. injecting in <head>, an additional and optional property like link or something would probably help. You need name and value anyway but we currently have nothing to deal with separate stylesheets.

@HadrienGardeur
Copy link
Contributor Author

I think the way we inject <link> for both CSS and JS also needs its own abstract model, similar to what we're discussing for user settings. Right now, this lacks the flexibility and modularity that we're looking for.

@JayPanoz
Copy link

Yeah will open an issue in ReadiumCSS as well, as the “build” process currently covers only one case i.e. setting inline styles on <html> and there is no way you can compile each user setting, it’s all or nothing.

@JayPanoz
Copy link

JayPanoz commented Jun 21, 2018

Post Engineering Call Recap

We have 3 different options there:

  1. injecting stylesheets using <link>;
  2. injecting styles using <style>;
  3. injecting inline styles i.e. <html style="">.

We’re currently using option 3. It’s worth noting option 1 and 2 still require custom properties to be set. Here’s the pros and cons for each option.

Injecting stylesheets using <link>

It’s basically about using a static representation* of ReadiumCSS user settings submodules dynamically. You append a <link> when the user sets a preference and remove it when the user resets this pref.

* Those have to be “compiled” using an altered version of our PostCSS config first.

For the pref to work, you still need to use CSS custom properties for settings which value can be incremented/decrement and/or CSS classes for static values e.g. reading modes.

Examples

<html style="name; value">
  <head>
     …
    <link href="stylesheet" id="ref" type="text/css" rel="stylesheet" />
  <head>
  <body>
    …
  </body>
</html>

or

<html>
  <head>
     …
    <link href="stylesheet" id="ref" type="text/css" rel="stylesheet" />
    <style type="text/css" id="ref-prop">
      :root { name: value; }
    </style>
  <head>
  <body>
    …
  </body>
</html>

Pros:

  • That should guarantee a reflow in rendering engines (although CSS multicol may well call that into question in some circumstances)
  • No need for flags/complex selectors/etc. to handle user prefs
  • in theory, would work a little bit better with HTTP2 – this is not necessarily the case in practice right now

Cons:

  • You must keep track of injected stylesheets in order to remove them later
  • You now have to handle both a <link> and a custom property
  • Since selectors are simpler in the stylesheet, their specificity is lower, which means more authors’ stylesheets may well override user settings
  • Must work in combination with 2 or 3 to set the custom property (unless there is another way)

injecting styles using <style>

It’s basically about writing styles directly in the DOM instead of linking to a css file. You append a <script> with the needed styles when the user sets a preference and remove it when the user resets this pref.

For the pref to work, you still need to use CSS custom properties for settings which value can be incremented/decrement and/or CSS classes for static values e.g. reading modes.

Examples

<html>
  <head>
     …
    <style type="text/css" id="ref">
      :root { name: value }
      html {
        font-size: var(name) !important;
      }
    </style>
  <head>
  <body>
    …
  </body>
</html>

Note that’s the simplest example, most user settings have much larger styles to write.

Pros:

  • That should guarantee a reflow in rendering engines (although CSS multicol may well call that into question in some circumstances)
  • No need for flags/complex selectors/etc. to handle user prefs
  • In theory, you can inject that anywhere, even at the very end of <body>
  • You can work without custom properties as you could very well use template stylesheets with placeholders for the value

Cons:

  • You must keep track of <style> elements in order to remove them later
  • Strong binding i.e. you must write the styles instead of linking to them (how do you implement CSS updates? etc.)
  • Since selectors are simpler in the stylesheet, their specificity is lower, which means more authors’ stylesheets may well override user settings
  • When you need to change the value of the custom prop, it’s unclear whether modifying the value in the existing <style> is a better option than removing it and re-appending it.

injecting inline styles i.e. <html style="">

It’s basically about adding, updating and removing custom properties in the style attribute of the <html> element. The ReadiumCSS-after.css stylesheet takes care of everything else. It’s worth noting we could do with static representations of the ReadiumCSS user submodules instead of bundling them.

Examples

<html style="name; value">
  <head>
     …
  <head>
  <body>
    …
  </body>
</html>

Pros:

  • Logic is scoped to the <html> element, everything else is taken care of in CSS
  • Even though it’s a side-effect, it forces us to have selectors with a higher specificity, which helps reduce the risk of authors’ stylesheets overriding/breaking user settings
  • Rendering engines may use performance tricks to make that super fast

Cons:

  • We need to use CSS tricks to force reflow/relayout – it’s unclear we wouldn’t need some with the 2 previous options though
  • Those are hooks authors can use i.e. they can use the name or value in selectors to target ReadiumCSS
  • We need flags (some settings are requiring 2 custom props)

Please feel free if I missed anything or if it’s unclear. Lists of pros and cons are by no means exhaustive so please feel free to add items to them.

@mickael-menu mickael-menu transferred this issue from readium/r2-streamer-swift Aug 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants