nicegui
nicegui copied to clipboard
Allow easy configuration for page background
As discussed in #407 it would be nice to have the possibility to set the background color to ui.colors().
Is anyone interested in implementing this? Should be fairly easy with the code provided in #407. It would be a good way to get yourself acquainted with the code base.
Maybe something like this?
colors.js
export default {
template: '<span style="display:none"></span>',
mounted() {
for (let name in this.$props) {
document.body.style.setProperty("--q-" + name, this.$props[name]);
};
document.body.style.setProperty("background-color", this.$props['background-color']);
},
props: {
primary: String,
secondary: String,
accent: String,
positive: String,
negative: String,
info: String,
warning: String,
background-color: String,
},
};
colors.py
from ..dependencies import register_component
from ..element import Element
register_component('colors', __file__, 'colors.js')
class Colors(Element):
def __init__(self, *,
primary='#5898d4',
secondary='#26a69a',
accent='#9c27b0',
positive='#21ba45',
negative='#c10015',
info='#31ccec',
warning='#f2c037'
background-color='white') -> None:
"""Color Theming
Sets the main colors (primary, secondary, accent, ...) used by `Quasar <https://quasar.dev/>`_.
"""
super().__init__('colors')
self._props['primary'] = primary
self._props['secondary'] = secondary
self._props['accent'] = accent
self._props['positive'] = positive
self._props['negative'] = negative
self._props['info'] = info
self._props['warning'] = warning
self._props['background-color'] = background-color
self.update()
Looks good. Could you create a pull request? This would honor your contribution for all eternity :-)
Looks good. Could you create a pull request? This would honor your contribution for all eternity :-)
It doesn't work, I'm looking for another solution. For some reason it isn't taking.
Okay I found a solution, I don't know if it is the best. You only need to change the color.py file.
from ..dependencies import register_component
from ..element import Element
from .. import globals
register_component('colors', __file__, 'colors.js')
class Colors(Element):
def __init__(self, *,
primary='#5898d4',
secondary='#26a69a',
accent='#9c27b0',
positive='#21ba45',
negative='#c10015',
info='#31ccec',
warning='#f2c037',
background='white') -> None:
"""Color Theming
Sets the main colors (primary, secondary, accent, ...) used by `Quasar <https://quasar.dev/>`_.
"""
super().__init__('colors')
self._props['primary'] = primary
self._props['secondary'] = secondary
self._props['accent'] = accent
self._props['positive'] = positive
self._props['negative'] = negative
self._props['info'] = info
self._props['warning'] = warning
self._set_background_color(background)
self.update()
def _set_background_color(self, code):
globals.get_client().head_html += f"<style>body {{ background-color: {code} }}</style>" + '\n'
I think it would be better to use the Quasar color palette and use an Enum of all the possible colors.
https://quasar.dev/style/color-palette#introduction
What would you think about something like this?
from ..dependencies import register_component
from ..element import Element
from .. import globals
register_component('colors', __file__, 'colors.js')
class Colors(Element):
def __init__(self, *,
primary='#5898d4',
secondary='#26a69a',
accent='#9c27b0',
positive='#21ba45',
negative='#c10015',
info='#31ccec',
warning='#f2c037',
background='white') -> None:
"""Color Theming
Sets the main colors (primary, secondary, accent, ...) used by `Quasar <https://quasar.dev/>`_.
"""
super().__init__('colors')
self._props['primary'] = primary
self._props['secondary'] = secondary
self._props['accent'] = accent
self._props['positive'] = positive
self._props['negative'] = negative
self._props['info'] = info
self._props['warning'] = warning
self._set_background(background)
self.update()
def _set_background(self, color):
globals.get_client().head_html += f"""<body class={color.value}></body>""" + '\n'
Interesting. Its a bit frustrating that Quasar does not provide it's colors for normal css (eg. background-color). Something like
<style>body {background-color: blue-4; }</style>
does not work. I guess that's why you suggested the introduction of QuasarColors. This may affect #117 and specifically the suggested implementation from PR #364. I would like to not mix the two topics if possible. Can we not make the colors available in css in some way? Or maybe we can lookup the color somehow in the ui.colors
implementation and then internally set the hex code according to the provided string?
Interesting. Its a bit frustrating that Quasar does not provide it's colors for normal css (eg. background-color). Something like
<style>body {background-color: blue-4; }</style>
does not work. I guess that's why you suggested the introduction of QuasarColors. This may affect #117 and specifically the suggested implementation from PR #364. I would like to not mix the two topics if possible. Can we not make the colors available in css in some way? Or maybe we can lookup the color somehow in the
ui.colors
implementation and then internally set the hex code according to the provided string?
We can have a dict that returns the hex value for the Quasar colors in the _set_background() function.
We can also have a regex that checks for RGB or HEX format so it's super flexible.
The enum can be inside the colors.py.
Sounds good
How about this?
from nicegui import ui
ui.colors(background="red-14") # works
ui.colors(background="#f44336") # works
ui.colors(background="rgb(244,67,54)") # works
ui.colors(background="pizza") # fails with no errors
with ui.card().classes("m-auto").style("width: 500px"):
ui.label("Background Test").classes("text-h3 m-auto")
ui.run()
from ..dependencies import register_component
from ..element import Element
from .. import globals
import re
rgb_pattern = re.compile(r'^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$')
hex_pattern = re.compile(r'^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$')
register_component('colors', __file__, 'colors.js')
class Colors(Element):
def __init__(self, *,
primary='#5898d4',
secondary='#26a69a',
accent='#9c27b0',
positive='#21ba45',
negative='#c10015',
info='#31ccec',
warning='#f2c037',
background='white') -> None:
"""Color Theming
Sets the main colors (primary, secondary, accent, ...) used by `Quasar <https://quasar.dev/>`_.
"""
super().__init__('colors')
self._props['primary'] = primary
self._props['secondary'] = secondary
self._props['accent'] = accent
self._props['positive'] = positive
self._props['negative'] = negative
self._props['info'] = info
self._props['warning'] = warning
self._set_background(background)
self.update()
def _set_background(self, color):
if color is not None:
if rgb_pattern.match(color):
globals.get_client().head_html += f"<style>body {{ background-color: {color} }}</style>" + '\n'
elif hex_pattern.match(color):
globals.get_client().head_html += f"<style>body {{ background-color: {color} }}</style>" + '\n'
else:
globals.get_client().head_html += f"<body class=bg-{color}></body>" + '\n'
does this work when dark theme is True ?
does this work when dark theme is True ?
That's a great question. I'll test it out later.
when I tried
ui.add_head_html('<style>body {background-color: #81D4FA; }</style>')
I had to make dark theme False
when I tried
ui.add_head_html('<style>body {background-color: #81D4FA; }</style>')
I had to make dark theme False
So dark mode overrides it? That is fine. There is a Quasar stylus variable that controls dark mode background color.
when I tried
ui.add_head_html('<style>body {background-color: #81D4FA; }</style>')
I had to make dark theme FalseSo dark mode overrides it? That is fine. There is a Quasar stylus variable that controls dark mode background color.
yes
Sounds good
Is this fine? It accepts the rgb(), hex and quasar formats. Also, if there is a incorrect color value, it doesn't error.
def _set_background(self, color):
if color is not None:
if rgb_pattern.match(color):
globals.get_client().head_html += f"<style>body {{ background-color: {color} }}</style>" + '\n'
elif hex_pattern.match(color):
globals.get_client().head_html += f"<style>body {{ background-color: {color} }}</style>" + '\n'
else:
globals.get_client().head_html += f"<body class=bg-{color}></body>" + '\n'
I have some concerns about this approach:
- Until now,
ui.colors
only manipulates CSS variables. The background color would be quite an exception. But ok, as long as the user doesn't need to known such implementation details, why not. - The colors in
ui.colors
should behave consistently. But now some colors "auto-detect" CSS vs. Quasar classes, others don't. That's unexpected from the user's perspective. - Adding a body tag into the head tag (what is done in the
else
block) is invalid HTML. - Toggling from "red-4" to "#ffffff" does not work, because the Quasar class
bg-red-4
takes precedence over the style definition. But the user might want to change the background color programmatically. - Setting a CSS color name does not work. For the regex (and the user!) there is not even a chance to distinguish between CSS "red" and Quasar's "red". But the color values are different and all other colors accept CSS color names. In my point of view this makes the "auto-detection" of CSS names vs. Quasar classes impossible.
I even wonder if extending ui.colors
is the right approach. Next time the user might want to add a background image and we need a new way to access the body tag. On the other hand we already can access the page layout and modify it with .style()
, .classes
, and .props()
. This is technically not the same like styling the body tag, but the results are similar.
Long story short: Let's re-think whether and in which way we want to extend ui.colors
, or if there is another more generic way to style the HTML body.
I have some concerns about this approach:
- Until now,
ui.colors
only manipulates CSS variables. The background color would be quite an exception. But ok, as long as the user doesn't need to known such implementation details, why not.- The colors in
ui.colors
should behave consistently. But now some colors "auto-detect" CSS vs. Quasar classes, others don't. That's unexpected from the user's perspective.- Adding a body tag into the head tag (what is done in the
else
block) is invalid HTML.- Toggling from "red-4" to "#ffffff" does not work, because the Quasar class
bg-red-4
takes precedence over the style definition. But the user might want to change the background color programmatically.- Setting a CSS color name does not work. For the regex (and the user!) there is not even a chance to distinguish between CSS "red" and Quasar's "red". But the color values are different and all other colors accept CSS color names. In my point of view this makes the "auto-detection" of CSS names vs. Quasar classes impossible.
I even wonder if extending
ui.colors
is the right approach. Next time the user might want to add a background image and we need a new way to access the body tag. On the other hand we already can access the page layout and modify it with.style()
,.classes
, and.props()
. This is technically not the same like styling the body tag, but the results are similar.Long story short: Let's re-think whether and in which way we want to extend
ui.colors
, or if there is another more generic way to style the HTML body.
Great points. For now, what is the best way to change the background color?
@Allen-Taylor See #407 for a discussion and two approaches with the current version of NiceGUI. Which one is better depends on the application, I guess. Personally I would simply stick with the accepted answer:
ui.add_head_html('<style>body {background-color: #81D4FA; }</style>')
Is there a special reason why the background control
is under ui.colors
?
Can't we implement a ui.background
element to control everything related to the app's background?
The ui.background
would be specialized, we could have different 'initiators', like: color
, image
or video
I like the approach @Diegiwg has taken with PR #433. But it does not support setting Quasar colors yet. Instead of duplicating the palette in Python we could use hex_color = await ui.run_javascript(f'Quasar.colors.getPaletteColor("{color}")')
to compute the background color on the fly. It seems that the JS function getPaletteColor
returns #000000
if it's not a Quasar color. So hex_color = hex_color if hex_color != '#000000' else color
should also enable passing any other css color.
I reviewed @Diegiwg's PR #433 and came up with an alternative implementation #448.
I gave both PR's some thought. While #411 is more generic it tries to mimic the behaviour of other elements. But it's a singleton (it will not create a new element like the other ones but just gives you access to the body tag). Thereby it behaves quite strange. For example
with ui.background():
ui.label('this is not shown')
I think deriving from ui.element is not the best approach. This thing is not an element. In my opinion it should have an different API.
Also the name is not perfect in my opinion. You can use it to set padding or font colors for the whole document which is nice but does not relate well to "background".
We have some other topics which may relate to this:
- existing functions which access/manipulate the body-tag
-
ui.colors
-
ui.add_body_html
-
- #117
- #364
I still can't wrap my head around this all.
We discussed this issue once more and noticed the following shortcomings of #411:
- The
ui.background
element suggests to be a regularElement
, but you can't insert child elements, for example. - The name "background" is not optimal, because you can affect more than just the background.
- The background element is limited to manipulating the body tag. But one might want to access the head or html tags as well.
- The current implementation does not work on individual pages built with a
ui.page
decorator, because the background element is a server-wide singleton.
Therefore we came up with the following new idea as an extension of #411:
- We introduce a
ui.query(selector: str)
that returns aQuery
element which basically holds its CSS selector string. - Multiple queries on the same client with the same selector string return the same
Query
element. - A
Query
has methodsadd_classes
,remove_classes
,add_style
,remove_style
,add_props
,remove_props
,append(html: str)
,prepend(html: str)
, and maybe more to modify the queried HTML elements. - Once
ui.query
is implemented,ui.colors
can be simplified usingui.query
. It can also allow setting the background color so that we can control all colors with one function.
The main challenge is managing the query elements, i.e. avoid duplicates or leftovers after a client disconnects. The mechanism for regular elements would work, but - as mentioned above - we would like ui.query
not to be a regular element. Maybe it's a function that looks into the current client's elements
dictionary and wraps it into a Query
object.