From 57784797e589db167785d64d106730b9dd49ca8f Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Thu, 13 Jul 2023 19:33:45 +0200 Subject: [PATCH 1/8] start to adjust website for dark mode --- main.py | 10 +++++----- website/example_card.py | 10 +++++----- website/static/style.css | 17 +++++++++++++++++ website/style.py | 6 +++--- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/main.py b/main.py index 98919257d..34a3f07d2 100755 --- a/main.py +++ b/main.py @@ -108,7 +108,7 @@ async def index_page(client: Client) -> None: add_header() with ui.row().classes('w-full h-screen items-center gap-8 pr-4 no-wrap into-section'): - svg.face(half=True).classes('stroke-black w-[200px] md:w-[230px] lg:w-[300px]') + svg.face(half=True).classes('stroke-black dark:stroke-white w-[200px] md:w-[230px] lg:w-[300px]') with ui.column().classes('gap-4 md:gap-8 pt-32'): title('Meet the *NiceGUI*.') subtitle('And let any browser be the frontend of your Python code.') \ @@ -244,8 +244,8 @@ async def index_page(client: Client) -> None: .classes('text-white text-2xl md:text-3xl font-medium') ui.html('Fun-Fact: This whole website is also coded with NiceGUI.') \ .classes('text-white text-lg md:text-xl') - ui.link('Documentation', '/documentation') \ - .classes('rounded-full mx-auto px-12 py-2 text-white bg-white font-medium text-lg md:text-xl') + ui.link('Documentation', '/documentation').style('color: black !important') \ + .classes('rounded-full mx-auto px-12 py-2 bg-white font-medium text-lg md:text-xl') with ui.column().classes('w-full p-8 lg:p-16 max-w-[1600px] mx-auto'): link_target('examples', '-50px') @@ -286,7 +286,7 @@ async def index_page(client: Client) -> None: example_link('Lightbox', 'A thumbnail gallery where each image can be clicked to enlarge') example_link('ROS2', 'Using NiceGUI as web interface for a ROS2 robot') - with ui.row().classes('bg-primary w-full min-h-screen mt-16'): + with ui.row().classes('dark-box min-h-screen mt-16'): link_target('why') with ui.column().classes(''' max-w-[1600px] m-auto @@ -367,4 +367,4 @@ async def documentation_page_more(name: str, client: Client) -> None: await client.connected() await ui.run_javascript(f'document.title = "{name} • NiceGUI";', respond=False) -ui.run(uvicorn_reload_includes='*.py, *.css, *.html') +ui.run(uvicorn_reload_includes='*.py, *.css, *.html', dark=None) diff --git a/website/example_card.py b/website/example_card.py index 5d88408ed..1cb497ac8 100644 --- a/website/example_card.py +++ b/website/example_card.py @@ -8,7 +8,7 @@ def create() -> None: with ui.card().style(r'clip-path: polygon(0 0, 100% 0, 100% 90%, 0 100%)') \ .classes('pb-16 no-shadow'), ui.row().classes('no-wrap'): with ui.column().classes('items-center'): - svg.face().classes('w-16 mx-6 stroke-black stroke-2') \ + svg.face().classes('w-16 mx-6 stroke-black dark:stroke-gray-100 stroke-2') \ .on('click', lambda _: output.set_text("That's my face!"), []) ui.button('Click me!', on_click=lambda: output.set_text('Clicked')).classes('w-full') ui.input('Text', value='abc', on_change=lambda e: output.set_text(e.value)) @@ -16,8 +16,8 @@ def create() -> None: ui.switch('Switch', on_change=lambda e: output.set_text('Switched on' if e.value else 'Switched off')) with ui.column().classes('items-center'): - output = ui.label('Try it out!') \ - .classes('w-44 my-6 h-8 text-xl text-grey-9 overflow-hidden text-ellipsis text-center') + output = ui.label('Try it out!').classes( + 'w-44 my-6 h-8 text-xl text-gray-800 dark:text-gray-200 overflow-hidden text-ellipsis text-center') ui.slider(min=0, max=100, value=50, step=0.1, on_change=lambda e: output.set_text(e.value)) \ .style('width: 150px; margin-bottom: 2px') with ui.row(): @@ -34,8 +34,8 @@ def create_narrow() -> None: .classes('pb-16 no-shadow'), ui.row().classes('no-wrap'): with ui.column().classes('items-center'): svg.face().classes('w-16 mx-6 stroke-black stroke-2').on('click', lambda _: output.set_text("That's my face!")) - output = ui.label('Try it out!') \ - .classes('w-44 my-6 h-8 text-xl text-grey-9 overflow-hidden text-ellipsis text-center') + output = ui.label('Try it out!').classes( + 'w-44 my-6 h-8 text-xl text-gray-800 dark:text-gray-200 overflow-hidden text-ellipsis text-center') ui.button('Click me!', on_click=lambda: output.set_text('Clicked')).classes('w-full') ui.input('Text', value='abc', on_change=lambda e: output.set_text(e.value)) diff --git a/website/static/style.css b/website/static/style.css index c1b4ed372..505dc601c 100644 --- a/website/static/style.css +++ b/website/static/style.css @@ -4,6 +4,10 @@ body { overflow-x: hidden; background-color: #f8f8f8; font-family: "Fira Sans", Roboto, -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif; + --q-dark-page: #222; +} +html:has(.body--dark) { + background-color: #222; } .browser-window { font-family: Roboto, -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif; @@ -46,6 +50,12 @@ a:active:not(.browser-window *) { background-color: #5898d4d0; backdrop-filter: blur(5px); } +.body--dark .q-header { + background-color: #3e6a94; +} +.body--dark .q-header.fade { + background-color: #3e6a94d0; +} .scroll-indicator:after { content: ""; @@ -61,6 +71,10 @@ a:active:not(.browser-window *) { animation: sdb04 1.5s infinite; transition-timing-function: ease; } +.body--dark .scroll-indicator:after { + border-left: 3px solid #bbb; + border-bottom: 3px solid #bbb; +} @-webkit-keyframes sdb04 { 0% { -webkit-transform: rotate(-45deg) translate(0, 0); @@ -109,6 +123,9 @@ dl.docinfo p { background-color: #5898d4; width: 100%; } +.body--dark .dark-box { + background-color: #3e6a94; +} @media only screen and (min-width: 1024px) { html { diff --git a/website/style.py b/website/style.py index fc2bafa63..cecb24d39 100644 --- a/website/style.py +++ b/website/style.py @@ -35,8 +35,8 @@ def example_link(title: str, description: str) -> None: with ui.link(target=f'https://github.com/zauberzeug/nicegui/tree/main/examples/{name}/{filename}') \ .classes('bg-[#5898d420] p-4 self-stretch rounded flex flex-col gap-2') \ .style('box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1)'): - ui.label(title).classes(replace='text-black font-bold') - ui.markdown(description).classes(replace='text-black bold-links arrow-links') + ui.label(title).classes(replace='font-bold') + ui.markdown(description).classes(replace='bold-links arrow-links') def features(icon: str, title: str, items: List[str]) -> None: @@ -49,5 +49,5 @@ def features(icon: str, title: str, items: List[str]) -> None: def side_menu() -> ui.left_drawer: return ui.left_drawer() \ - .classes('column no-wrap gap-1 bg-[#eee] mt-[-20px] px-8 py-20') \ + .classes('column no-wrap gap-1 bg-[#eee] dark:bg-[#1b1b1b] mt-[-20px] px-8 py-20') \ .style('height: calc(100% + 20px) !important') From da2471af2e836328246182ce06fec19f22fd7bb6 Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Thu, 13 Jul 2023 20:30:10 +0200 Subject: [PATCH 2/8] add dark mode switch to header --- main.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index 34a3f07d2..39dc67849 100755 --- a/main.py +++ b/main.py @@ -74,6 +74,7 @@ def add_header(menu: Optional[ui.left_drawer] = None) -> None: 'Examples': '/#examples', 'Why?': '/#why', } + dark_mode = ui.dark_mode().bind_value(app.storage.user, 'dark_mode') with ui.header() \ .classes('items-center duration-200 p-0 px-4 no-wrap') \ .style('box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)'): @@ -82,19 +83,32 @@ def add_header(menu: Optional[ui.left_drawer] = None) -> None: with ui.link(target=index_page).classes('row gap-4 items-center no-wrap mr-auto'): svg.face().classes('w-8 stroke-white stroke-2 max-[550px]:hidden') svg.word().classes('w-24') - with ui.row().classes('max-lg:hidden'): + + with ui.row().classes('max-[1050px]:hidden'): for title, target in menu_items.items(): ui.link(title, target).classes(replace='text-lg text-white') + search = Search() search.create_button() - with ui.link(target='https://discord.gg/TEpFeAaF4f').classes('max-[445px]:hidden').tooltip('Discord'): + + with ui.element().classes('max-[360px]:hidden'): + ui.button(icon='dark_mode', on_click=lambda: dark_mode.set_value(None)) \ + .props('flat fab-mini color=white').bind_visibility_from(dark_mode, 'value', value=True) + ui.button(icon='light_mode', on_click=lambda: dark_mode.set_value(True)) \ + .props('flat fab-mini color=white').bind_visibility_from(dark_mode, 'value', value=False) + ui.button(icon='brightness_auto', on_click=lambda: dark_mode.set_value(False)) \ + .props('flat fab-mini color=white').bind_visibility_from(dark_mode, 'value', lambda mode: mode is None) + + with ui.link(target='https://discord.gg/TEpFeAaF4f').classes('max-[455px]:hidden').tooltip('Discord'): svg.discord().classes('fill-white scale-125 m-1') - with ui.link(target='https://www.reddit.com/r/nicegui/').classes('max-[395px]:hidden').tooltip('Reddit'): + with ui.link(target='https://www.reddit.com/r/nicegui/').classes('max-[405px]:hidden').tooltip('Reddit'): svg.reddit().classes('fill-white scale-125 m-1') - with ui.link(target='https://github.com/zauberzeug/nicegui/').tooltip('GitHub'): + with ui.link(target='https://github.com/zauberzeug/nicegui/').classes('max-[305px]:hidden').tooltip('GitHub'): svg.github().classes('fill-white scale-125 m-1') + add_star().classes('max-[490px]:hidden') - with ui.row().classes('lg:hidden'): + + with ui.row().classes('min-[1051px]:hidden'): with ui.button(icon='more_vert').props('flat color=white round'): with ui.menu().classes('bg-primary text-white text-lg'): for title, target in menu_items.items(): @@ -367,4 +381,4 @@ async def documentation_page_more(name: str, client: Client) -> None: await client.connected() await ui.run_javascript(f'document.title = "{name} • NiceGUI";', respond=False) -ui.run(uvicorn_reload_includes='*.py, *.css, *.html', dark=None) +ui.run(uvicorn_reload_includes='*.py, *.css, *.html') From 1f2e637009f00f00c816ed8b432f864ad0379ae1 Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Fri, 14 Jul 2023 16:49:26 +0200 Subject: [PATCH 3/8] fix Tailwind configuration for automatic dark mode --- nicegui/elements/dark_mode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nicegui/elements/dark_mode.js b/nicegui/elements/dark_mode.js index 1d487938d..da627d1f7 100644 --- a/nicegui/elements/dark_mode.js +++ b/nicegui/elements/dark_mode.js @@ -9,7 +9,7 @@ export default { update() { Quasar.Dark.set(this.value === null ? "auto" : this.value); if (window.tailwind) { - tailwind.config.darkMode = this.auto ? "media" : "class"; + tailwind.config.darkMode = this.value === null ? "media" : "class"; if (this.value) document.body.classList.add("dark"); else document.body.classList.remove("dark"); } From 710ed138f380ff4fe2456d5c8ff080e402ec8e0f Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Fri, 14 Jul 2023 17:59:42 +0200 Subject: [PATCH 4/8] improve dark color theme for demo windows --- main.py | 2 +- website/demo.py | 66 ++++++++++++++++------------------------ website/documentation.py | 6 ---- 3 files changed, 27 insertions(+), 47 deletions(-) diff --git a/main.py b/main.py index 9d540aa81..7df70730e 100755 --- a/main.py +++ b/main.py @@ -74,7 +74,7 @@ def add_header(menu: Optional[ui.left_drawer] = None) -> None: 'Examples': '/#examples', 'Why?': '/#why', } - dark_mode = ui.dark_mode().bind_value(app.storage.user, 'dark_mode') + dark_mode = ui.dark_mode(value=None).bind_value(app.storage.user, 'dark_mode') with ui.header() \ .classes('items-center duration-200 p-0 px-4 no-wrap') \ .style('box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)'): diff --git a/website/demo.py b/website/demo.py index a2b8cd1e5..abdc0bd83 100644 --- a/website/demo.py +++ b/website/demo.py @@ -1,6 +1,6 @@ import inspect import re -from typing import Callable, Optional, Union +from typing import Callable, Literal, Optional, Union import isort @@ -8,20 +8,14 @@ from .intersection_observer import IntersectionObserver as intersection_observer -PYTHON_BGCOLOR = '#00000010' -PYTHON_COLOR = '#eef5fb' -BASH_BGCOLOR = '#00000010' -BASH_COLOR = '#e8e8e8' -BROWSER_BGCOLOR = '#00000010' -BROWSER_COLOR = '#ffffff' +WindowType = Literal['python', 'bash', 'browser'] - -uncomment_pattern = re.compile(r'^(\s*)# ?') +UNCOMMENT_PATTERN = re.compile(r'^(\s*)# ?') def uncomment(text: str) -> str: """non-executed lines should be shown in the code examples""" - return uncomment_pattern.sub(r'\1', text) + return UNCOMMENT_PATTERN.sub(r'\1', text) def demo(f: Callable) -> Callable: @@ -57,10 +51,6 @@ async def copy_code(): return f -def _window_header(bgcolor: str) -> ui.row(): - return ui.row().classes(f'w-full h-8 p-2 bg-[{bgcolor}]') - - def _dots() -> None: with ui.row().classes('gap-1 relative left-[1px] top-[1px]'): ui.icon('circle').classes('text-[13px] text-red-400') @@ -68,43 +58,39 @@ def _dots() -> None: ui.icon('circle').classes('text-[13px] text-green-400') -def _title(title: str) -> None: - ui.label(title).classes('text-sm text-gray-600 absolute left-1/2 top-[6px]').style('transform: translateX(-50%)') - - -def _tab(content: Union[str, Callable], color: str, bgcolor: str) -> None: - with ui.row().classes('gap-0'): - with ui.label().classes(f'w-2 h-[24px] bg-[{color}]'): - ui.label().classes(f'w-full h-full bg-[{bgcolor}] rounded-br-[6px]') - with ui.row().classes(f'text-sm text-gray-600 px-6 py-1 h-[24px] rounded-t-[6px] bg-[{color}] items-center gap-2'): - if callable(content): - content() - else: - ui.label(content) - with ui.label().classes(f'w-2 h-[24px] bg-[{color}]'): - ui.label().classes(f'w-full h-full bg-[{bgcolor}] rounded-bl-[6px]') - - -def window(color: str, bgcolor: str, *, - title: str = '', tab: Union[str, Callable] = '', classes: str = '') -> ui.column: - with ui.card().classes(f'no-wrap bg-[{color}] rounded-xl p-0 gap-0 {classes}') \ +def window(type: WindowType, *, title: str = '', tab: Union[str, Callable] = '', classes: str = '') -> ui.column: + bgcolor = ('#00000010', '#ffffff10') + color = { + 'python': ('#eef5fb', '#2b323b'), + 'bash': ('#e8e8e8', '#2b323b'), + 'browser': ('#ffffff', '#181c21'), + }[type] + with ui.card().classes(f'no-wrap bg-[{color[0]}] dark:bg-[{color[1]}] rounded-xl p-0 gap-0 {classes}') \ .style('box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1)'): - with _window_header(bgcolor): + with ui.row().classes(f'w-full h-8 p-2 bg-[{bgcolor[0]}] dark:bg-[{bgcolor[1]}]'): _dots() if title: - _title(title) + ui.label(title) \ + .classes('text-sm text-gray-600 dark:text-gray-400 absolute left-1/2 top-[6px]') \ + .style('transform: translateX(-50%)') if tab: - _tab(tab, color, bgcolor) + with ui.row().classes('gap-0'): + with ui.label().classes(f'w-2 h-[24px] bg-[{color[0]}] dark:bg-[{color[1]}]'): + ui.label().classes(f'w-full h-full bg-[{bgcolor[0]}] dark:bg-[{bgcolor[1]}] rounded-br-[6px]') + with ui.row().classes(f'text-sm text-gray-600 dark:text-gray-400 px-6 py-1 h-[24px] rounded-t-[6px] bg-[{color[0]}] dark:bg-[{color[1]}] items-center gap-2'): + tab() if callable(tab) else ui.label(tab) + with ui.label().classes(f'w-2 h-[24px] bg-[{color[0]}] dark:bg-[{color[1]}]'): + ui.label().classes(f'w-full h-full bg-[{bgcolor[0]}] dark:bg-[{bgcolor[1]}] rounded-bl-[6px]') return ui.column().classes('w-full h-full overflow-auto') def python_window(title: Optional[str] = None, *, classes: str = '') -> ui.card: - return window(PYTHON_COLOR, PYTHON_BGCOLOR, title=title or 'main.py', classes=classes).classes('p-2 python-window') + return window('python', title=title or 'main.py', classes=classes).classes('p-2 python-window') def bash_window(*, classes: str = '') -> ui.card: - return window(BASH_COLOR, BASH_BGCOLOR, title='bash', classes=classes).classes('p-2 bash-window') + return window('bash', title='bash', classes=classes).classes('p-2 bash-window') def browser_window(title: Optional[Union[str, Callable]] = None, *, classes: str = '') -> ui.card: - return window(BROWSER_COLOR, BROWSER_BGCOLOR, tab=title or 'NiceGUI', classes=classes).classes('p-4 browser-window') + return window('browser', tab=title or 'NiceGUI', classes=classes).classes('p-4 browser-window') diff --git a/website/documentation.py b/website/documentation.py index bbbd4d1fa..19355fdb4 100644 --- a/website/documentation.py +++ b/website/documentation.py @@ -548,10 +548,6 @@ def auto_context_demo(): load_demo(ui.run) - # HACK: switch color to white for the next demo - demo_BROWSER_BGCOLOR = demo.BROWSER_BGCOLOR - demo.BROWSER_BGCOLOR = '#ffffff' - @text_demo('Native Mode', ''' You can enable native mode for NiceGUI by specifying `native=True` in the `ui.run` function. To customize the initial window size and display mode, use the `window_size` and `fullscreen` parameters respectively. @@ -575,8 +571,6 @@ def native_mode_demo(): # ui.run(native=True, window_size=(400, 300), fullscreen=False) # END OF DEMO ui.button('enlarge', on_click=lambda: ui.notify('window will be set to 1000x700 in native mode')) - # HACK: restore color - demo.BROWSER_BGCOLOR = demo_BROWSER_BGCOLOR # Show a helpful workaround until issue is fixed upstream. # For more info see: https://github.com/r0x0r/pywebview/issues/1078 From e56c73b95fef6c8b35531c265d652001560ddad7 Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Fri, 14 Jul 2023 18:01:27 +0200 Subject: [PATCH 5/8] support change handler for ui.dark_mode --- nicegui/elements/dark_mode.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/nicegui/elements/dark_mode.py b/nicegui/elements/dark_mode.py index eddab8f00..eba7b9136 100644 --- a/nicegui/elements/dark_mode.py +++ b/nicegui/elements/dark_mode.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Any, Callable, Optional from .mixins.value_element import ValueElement @@ -6,7 +6,7 @@ class DarkMode(ValueElement, component='dark_mode.js'): VALUE_PROP = 'value' - def __init__(self, value: Optional[bool] = False) -> None: + def __init__(self, value: Optional[bool] = False, *, on_change: Optional[Callable[..., Any]] = None) -> None: """Dark mode You can use this element to enable, disable or toggle dark mode on the page. @@ -15,8 +15,9 @@ def __init__(self, value: Optional[bool] = False) -> None: Note that this element overrides the `dark` parameter of the `ui.run` function and page decorators. :param value: Whether dark mode is enabled. If None, dark mode is set to auto. + :param on_change: Callback that is invoked when the value changes. """ - super().__init__(value=value, on_value_change=None) + super().__init__(value=value, on_value_change=on_change) def enable(self) -> None: """Enable dark mode.""" From a817bf2594c8d9209915e38d3b9f7f340fb984fa Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Fri, 14 Jul 2023 18:16:39 +0200 Subject: [PATCH 6/8] automatically use dark theme for syntax highlighting in ui.markdown --- nicegui/elements/markdown.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nicegui/elements/markdown.py b/nicegui/elements/markdown.py index afb4db484..25c0a7f26 100644 --- a/nicegui/elements/markdown.py +++ b/nicegui/elements/markdown.py @@ -23,7 +23,10 @@ def __init__(self, content: str = '', *, extras: List[str] = ['fenced-code-block self.extras = extras super().__init__(content=content) self._classes = ['nicegui-markdown'] - self._props['codehilite_css'] = HtmlFormatter(nobackground=True).get_style_defs('.codehilite') + self._props['codehilite_css'] = ( + HtmlFormatter(nobackground=True).get_style_defs('.codehilite') + + HtmlFormatter(nobackground=True, style='github-dark').get_style_defs('.body--dark .codehilite') + ) if 'mermaid' in extras: self._props['use_mermaid'] = True self.libraries.append(Mermaid.exposed_libraries[0]) From b6df1f1d88a831fe223365ebbb83039977927cc6 Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Sun, 16 Jul 2023 16:18:20 +0200 Subject: [PATCH 7/8] fix ui.dark_mode demo --- website/demo.py | 22 +++++++++++-------- .../dark_mode_documentation.py | 12 ++++++++-- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/website/demo.py b/website/demo.py index abdc0bd83..06c4bf0e5 100644 --- a/website/demo.py +++ b/website/demo.py @@ -12,6 +12,12 @@ UNCOMMENT_PATTERN = re.compile(r'^(\s*)# ?') +WINDOW_BG_COLORS = { + 'python': ('#eef5fb', '#2b323b'), + 'bash': ('#e8e8e8', '#2b323b'), + 'browser': ('#ffffff', '#181c21'), +} + def uncomment(text: str) -> str: """non-executed lines should be shown in the code examples""" @@ -59,15 +65,11 @@ def _dots() -> None: def window(type: WindowType, *, title: str = '', tab: Union[str, Callable] = '', classes: str = '') -> ui.column: - bgcolor = ('#00000010', '#ffffff10') - color = { - 'python': ('#eef5fb', '#2b323b'), - 'bash': ('#e8e8e8', '#2b323b'), - 'browser': ('#ffffff', '#181c21'), - }[type] + bar_color = ('#00000010', '#ffffff10') + color = WINDOW_BG_COLORS[type] with ui.card().classes(f'no-wrap bg-[{color[0]}] dark:bg-[{color[1]}] rounded-xl p-0 gap-0 {classes}') \ .style('box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1)'): - with ui.row().classes(f'w-full h-8 p-2 bg-[{bgcolor[0]}] dark:bg-[{bgcolor[1]}]'): + with ui.row().classes(f'w-full h-8 p-2 bg-[{bar_color[0]}] dark:bg-[{bar_color[1]}]'): _dots() if title: ui.label(title) \ @@ -76,11 +78,13 @@ def window(type: WindowType, *, title: str = '', tab: Union[str, Callable] = '', if tab: with ui.row().classes('gap-0'): with ui.label().classes(f'w-2 h-[24px] bg-[{color[0]}] dark:bg-[{color[1]}]'): - ui.label().classes(f'w-full h-full bg-[{bgcolor[0]}] dark:bg-[{bgcolor[1]}] rounded-br-[6px]') + ui.label().classes( + f'w-full h-full bg-[{bar_color[0]}] dark:bg-[{bar_color[1]}] rounded-br-[6px]') with ui.row().classes(f'text-sm text-gray-600 dark:text-gray-400 px-6 py-1 h-[24px] rounded-t-[6px] bg-[{color[0]}] dark:bg-[{color[1]}] items-center gap-2'): tab() if callable(tab) else ui.label(tab) with ui.label().classes(f'w-2 h-[24px] bg-[{color[0]}] dark:bg-[{color[1]}]'): - ui.label().classes(f'w-full h-full bg-[{bgcolor[0]}] dark:bg-[{bgcolor[1]}] rounded-bl-[6px]') + ui.label().classes( + f'w-full h-full bg-[{bar_color[0]}] dark:bg-[{bar_color[1]}] rounded-bl-[6px]') return ui.column().classes('w-full h-full overflow-auto') diff --git a/website/more_documentation/dark_mode_documentation.py b/website/more_documentation/dark_mode_documentation.py index f62b49482..784243fe8 100644 --- a/website/more_documentation/dark_mode_documentation.py +++ b/website/more_documentation/dark_mode_documentation.py @@ -1,5 +1,7 @@ from nicegui import ui +from ..demo import WINDOW_BG_COLORS + def main_demo() -> None: # dark = ui.dark_mode() @@ -9,5 +11,11 @@ def main_demo() -> None: # END OF DEMO l = ui.label('Switch mode:') c = l.parent_slot.parent - ui.button('Dark', on_click=lambda: (l.style('color: white'), c.style('background-color: var(--q-dark-page)'))) - ui.button('Light', on_click=lambda: (l.style('color: default'), c.style('background-color: default'))) + ui.button('Dark', on_click=lambda: ( + l.style('color: white'), + c.style(f'background-color: {WINDOW_BG_COLORS["browser"][1]}'), + )) + ui.button('Light', on_click=lambda: ( + l.style('color: black'), + c.style(f'background-color: {WINDOW_BG_COLORS["browser"][0]}'), + )) From 73de652d701cdf9b43ae9a69323d1f301e4d1e74 Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Mon, 17 Jul 2023 12:06:26 +0200 Subject: [PATCH 8/8] store dark mode setting in browser --- main.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 7df70730e..5db321fc3 100755 --- a/main.py +++ b/main.py @@ -47,6 +47,11 @@ def logo_square() -> FileResponse: return FileResponse(svg.PATH / 'logo_square.png', media_type='image/png') +@app.post('/dark_mode') +async def dark_mode(request: Request) -> None: + app.storage.browser['dark_mode'] = (await request.json()).get('value') + + @app.middleware('http') async def redirect_reference_to_documentation(request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response: @@ -74,7 +79,13 @@ def add_header(menu: Optional[ui.left_drawer] = None) -> None: 'Examples': '/#examples', 'Why?': '/#why', } - dark_mode = ui.dark_mode(value=None).bind_value(app.storage.user, 'dark_mode') + dark_mode = ui.dark_mode(value=app.storage.browser.get('dark_mode'), on_change=lambda e: ui.run_javascript(f''' + fetch('/dark_mode', {{ + method: 'POST', + headers: {{'Content-Type': 'application/json'}}, + body: JSON.stringify({{value: {e.value}}}), + }}); + ''', respond=False)) with ui.header() \ .classes('items-center duration-200 p-0 px-4 no-wrap') \ .style('box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)'):