From 12f916455bb635b047cec04e30f8ee6f319639f6 Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Sun, 30 Aug 2026 01:22:03 -0500 Subject: [PATCH] nicegui styling --- src/personal_mcp/docs/skills/nicegui/SKILL.md | 32 +- .../references/colors-and-quasar-theming.md | 189 ++++++++++ .../nicegui/references/component-mechanics.md | 2 +- .../references/styling-and-customization.md | 325 ++++++++---------- 4 files changed, 355 insertions(+), 193 deletions(-) create mode 100644 src/personal_mcp/docs/skills/nicegui/references/colors-and-quasar-theming.md diff --git a/src/personal_mcp/docs/skills/nicegui/SKILL.md b/src/personal_mcp/docs/skills/nicegui/SKILL.md index 33417d5..d4d4b85 100644 --- a/src/personal_mcp/docs/skills/nicegui/SKILL.md +++ b/src/personal_mcp/docs/skills/nicegui/SKILL.md @@ -53,13 +53,24 @@ Load [FastAPI and Uvicorn startup](./references/fastapi-uvicorn-startup.md) for: Load [styling and customization](./references/styling-and-customization.md) for: -- app-wide and page-level color themes, dark mode, and semantic CSS tokens -- Tailwind and Quasar utility classes -- scoped CSS properties and stable application classes -- responsive page composition and static asset loading -- cosmetic treatment of controls, surfaces, typography, and visual states +- the precedence of NiceGUI Python mechanics, then Tailwind classes, then scoped CSS only as a fallback +- page shells, flex and grid tracks, stable dimensions, and overflow ownership +- Python composition APIs, Tailwind structural utilities, and Quasar-owned helpers +- font loading, family selection, type scales, line height, and line length +- responsive page composition, breakpoint boundaries, and static asset loading +- rem-based sizing, browser enlargement, CSS zoom, and transform scaling - visual validation at supported viewport sizes +### NiceGUI And Quasar Colors + +Load [NiceGUI and Quasar color theming](./references/colors-and-quasar-theming.md) for: + +- the boundary between NiceGUI's Python APIs and Quasar's browser color system +- app-wide and page-level palette scope and precedence +- semantic brand roles, fixed Quasar palette colors, and custom color names +- component color value classification across Quasar, Tailwind, and CSS +- the separation between palette values and dark-mode state + ### Component Mechanics Load [component mechanics](./references/component-mechanics.md) for: @@ -124,15 +135,16 @@ Load [source documentation](./references/source-documentation.md) when: 1. Load [application architecture](./references/architecture.md). 2. Add [FastAPI and Uvicorn startup](./references/fastapi-uvicorn-startup.md) when FastAPI owns the application or startup must be exposed as a project command. -3. Add [styling and customization](./references/styling-and-customization.md) only when page layout or visual customization is in scope. +3. Add [styling and customization](./references/styling-and-customization.md) only when page structure, typography, or scaling is in scope. ### Page Or Component Work 1. Load [application architecture](./references/architecture.md) for page and component ownership decisions. -2. Load [styling and customization](./references/styling-and-customization.md) for themes, layout, responsive presentation, utility classes, or CSS. -3. Load [component mechanics](./references/component-mechanics.md) when behavior must be mapped across NiceGUI, Quasar, and Vue, or when detached content and component-specific behavior are involved. -4. Load [editable tables](./references/tables.md) when table cells accept user changes or `QPopupEdit` is being considered. -5. Add [interaction patterns](./references/interaction-patterns.md) or [bindable dataclasses](./references/binding-dataclasses.md) according to the page behavior. +2. Load [component mechanics](./references/component-mechanics.md) when behavior must be mapped across NiceGUI, Quasar, and Vue, or when detached content and component-specific behavior are involved. +3. Load [styling and customization](./references/styling-and-customization.md) for page structure, typography, responsive presentation, utility classes, or scaling; preserve its Python-then-Tailwind precedence before adding CSS. +4. Add [NiceGUI and Quasar color theming](./references/colors-and-quasar-theming.md) when palette scope, color names, theme propagation, or dark-mode boundaries matter. +5. Load [editable tables](./references/tables.md) when table cells accept user changes or `QPopupEdit` is being considered. +6. Add [interaction patterns](./references/interaction-patterns.md) or [bindable dataclasses](./references/binding-dataclasses.md) according to the page behavior. ### Debugging Or Production Review diff --git a/src/personal_mcp/docs/skills/nicegui/references/colors-and-quasar-theming.md b/src/personal_mcp/docs/skills/nicegui/references/colors-and-quasar-theming.md new file mode 100644 index 0000000..d4b9cea --- /dev/null +++ b/src/personal_mcp/docs/skills/nicegui/references/colors-and-quasar-theming.md @@ -0,0 +1,189 @@ +# NiceGUI And Quasar Color Theming + +This reference describes how NiceGUI's Python color APIs map onto Quasar's browser-side color system. It distinguishes theme configuration from individual element colors, fixed palette colors from runtime brand roles, and palette values from dark-mode state. + +The primary public references are [NiceGUI styling and appearance](https://nicegui.io/documentation/section_styling_appearance), [NiceGUI color theming](https://nicegui.io/documentation/colors), and the [Quasar color palette](https://quasar.dev/style/color-palette). + +## Boundary At A Glance + +NiceGUI does not define an independent component theme engine. It configures and consumes the Quasar color system while adding Python-facing scope, value classification, and CSS cascade behavior. + +| Surface | NiceGUI owns | Quasar or the browser owns | +| --- | --- | --- | +| `app.colors(...)` | application-wide Python configuration and custom-name registration | initial Quasar brand configuration and the resulting `--q-*` values on each page | +| `ui.colors(...)` | a page-level element and precedence over `app.colors()` | runtime `--q-*` properties on `document.body` plus custom `text-*` and `bg-*` classes | +| component `color=` and `text_color=` arguments | classification of supported values as Quasar, Tailwind, or CSS colors on color-aware wrappers | rendering through a Quasar prop, a utility class, or an inline CSS declaration | +| `.props("color=...")` | transport of the prop to the frontend component | interpretation of the value by that Quasar component | +| `.classes("text-primary bg-positive")` | attachment of class names and NiceGUI's CSS layer arrangement | Quasar's semantic utility classes and their `--q-*` variable references | +| `ui.dark_mode(...)` | Python control and binding with `True`, `False`, or automatic `None` state | Quasar dark-mode state, `body--light` or `body--dark`, and dark-aware components | + +The central handoff is a CSS custom property. NiceGUI supplies a value such as `#176b5b`; Quasar components and helpers consume `var(--q-primary)`. + +## Quasar Color Namespaces + +Quasar exposes two materially different kinds of color name. Only one kind is changed by NiceGUI's theme APIs. + +### Runtime Brand Roles + +Quasar's semantic brand roles are backed by root or body-level CSS custom properties. Components and semantic utility classes follow these values at runtime. NiceGUI exposes the eight Quasar brand roles and the separate dark-page surface through `app.colors()` and `ui.colors()`. + +| NiceGUI argument | CSS custom property | NiceGUI default | Intended meaning | +| --- | --- | --- | --- | +| `primary` | `--q-primary` | `#5898d4` | main action and brand emphasis | +| `secondary` | `--q-secondary` | `#26a69a` | secondary brand emphasis | +| `accent` | `--q-accent` | `#9c27b0` | accent emphasis | +| `dark` | `--q-dark` | `#1d1d1d` | dark component surface | +| `dark_page` | `--q-dark-page` | `#121212` | dark page background | +| `positive` | `--q-positive` | `#21ba45` | success state | +| `negative` | `--q-negative` | `#c10015` | error or destructive state | +| `info` | `--q-info` | `#31ccec` | informational state | +| `warning` | `--q-warning` | `#f2c037` | warning state | + +For example, `color="primary"`, `.props("color=primary")`, `text-primary`, and `bg-primary` all reach Quasar's semantic primary role. Changing that role changes every consumer of `--q-primary`; it does not rewrite fixed palette colors. + +```python +from nicegui import app, ui + +app.colors( + primary="#176b5b", + secondary="#52645f", + accent="#c05a32", + positive="#2e7d32", + negative="#b3261e", + info="#276b8e", + warning="#a86600", + dark="#202523", + dark_page="#151917", +) + +ui.button("Save") +ui.label("Saved").classes("text-positive") +``` + +The current NiceGUI client implementation writes page-level values to `document.body` in [`colors.js`](https://github.com/zauberzeug/nicegui/blob/main/nicegui/elements/colors.js). Quasar's semantic helpers reference those properties, as described under [dynamic brand colors](https://quasar.dev/style/color-palette#dynamic-change-of-brand-colors-dynamic-theme-colors). + +### Fixed Palette Colors + +Names such as `red-5`, `teal-10`, and `blue-grey-2` belong to Quasar's compiled [color list](https://quasar.dev/style/color-palette#color-list). Their `text-*` and `bg-*` classes contain fixed color values rather than references to the semantic brand variables. + +Consequently: + +- `ui.colors(primary="#0057b8")` changes `primary`, `text-primary`, and `bg-primary` consumers. +- It does not change `blue`, `blue-6`, `text-blue-6`, or `bg-blue-6`. +- A fixed palette color can be assigned to a component, for example `ui.button("Open", color="teal-7")`, without adding it to the application theme. + +The fixed palette is a Quasar facility bundled into NiceGUI. It is not generated by `app.colors()` or `ui.colors()`. + +### Custom Semantic Names + +Extra keyword arguments create application-specific names: + +```python +from nicegui import app, ui + +app.colors(brand="#176b5b", review_required="#a86600") + +ui.button("Continue", color="brand") +ui.label("Review required").classes("text-review-required") +``` + +NiceGUI normalizes underscores in Python keyword names to hyphens in browser color names. For each custom name, the client-side [`applyColors`](https://github.com/zauberzeug/nicegui/blob/main/nicegui/static/nicegui.js) helper creates: + +- a `--q-` property on `document.body` +- a `.text-` class that reads that property +- a `.bg-` class that reads that property + +This automates the custom-class pattern shown in Quasar's [adding your own colors](https://quasar.dev/style/color-palette#adding-your-own-colors) reference. NiceGUI also registers the name in its Python-side Quasar color set so color-aware wrappers pass the value as a Quasar color prop. The name must therefore be declared with `app.colors()` or `ui.colors()` before a NiceGUI component first uses it; this ordering requirement is part of the [NiceGUI custom colors contract](https://nicegui.io/documentation/colors#custom_colors). + +## Scope And Precedence + +The effective palette has three levels: + +| Level | Scope | Effect | +| --- | --- | --- | +| bundled Quasar values | every page | fallback values supplied by Quasar's CSS | +| `app.colors(...)` | all NiceGUI pages | populates NiceGUI's Quasar brand configuration before each client app starts | +| `ui.colors(...)` | current page | writes the core and custom properties on that page's `document.body` and takes precedence over app-wide values | + +`app.colors()` is configuration, not a rendered UI element. NiceGUI stores its values in the application's Quasar configuration; see the current [`App.colors` implementation](https://github.com/zauberzeug/nicegui/blob/main/nicegui/app/app.py). + +`ui.colors()` is rendered into a specific page. Its DOM placement in a row, card, or other container does not scope the palette to that subtree because its client component writes to `document.body`. A page with two calls therefore has one effective page palette, with the last mounted call determining the core values. Subtree-specific theming requires application CSS variables or directly scoped `--q-*` overrides, not nested `ui.colors()` elements. + +The `ui.colors()` initializer supplies all nine core values. A call such as `ui.colors(primary="#555")` is therefore a complete core-palette assignment: unspecified roles resolve to NiceGUI's defaults rather than acting as a one-property patch over `app.colors()`. Pages that must retain customized app-wide secondary, status, or dark values should pass those values explicitly in the page override. + +`app.colors()` was added in NiceGUI 3.6.0, while custom colors were added to `ui.colors()` in 2.2.0. Applications pinned to earlier NiceGUI releases need version-matched behavior from the [NiceGUI colors reference](https://nicegui.io/documentation/colors). + +## Element Color Values + +On elements implemented with NiceGUI's color mixins, a `color`, `text_color`, or corresponding setter value is classified in this order by [`color_elements.py`](https://github.com/zauberzeug/nicegui/blob/main/nicegui/elements/mixins/color_elements.py): + +| Input kind | Example | NiceGUI output | Theme response | +| --- | --- | --- | --- | +| Quasar semantic, fixed, or registered custom name | `primary`, `red-5`, `brand` | Quasar component color prop | semantic and custom names follow `--q-*`; fixed names do not | +| recognized Tailwind color | `red-500` | `bg-red-500` or `text-red-500` class | independent of the Quasar palette | +| other CSS color value | `#ff0000`, `rgb(255 0 0)`, `rebeccapurple` | inline `background-color` or `color` | independent of the Quasar palette | +| `None` | `None` | removes the managed color | falls back to component and cascade defaults | + +This classification is a NiceGUI convenience, not a general Quasar rule. Passing `.props("color=#ff0000")` bypasses NiceGUI's color mixin and asks the Quasar component to interpret `#ff0000` as its `color` prop. Likewise, components that expose a raw Quasar color prop without using the mixin may accept only the values documented by that component. The specific NiceGUI constructor documentation remains authoritative for each element. + +Quasar and Tailwind color classes share the same HTML class list but not the same namespace conventions. `text-red-5` is a Quasar fixed-palette helper; `text-red-500` is a Tailwind-compatible utility. Semantic names such as `text-primary` are Quasar helpers. + +## Palette Values And Dark Mode Are Separate + +The `dark` and `dark_page` arguments define colors; they do not enable dark mode. Mode state is controlled by [`ui.dark_mode()`](https://nicegui.io/documentation/dark_mode), the `dark` argument of `ui.run()`, or a page decorator. `ui.dark_mode()` takes precedence for its page and maps `None` to Quasar's automatic system-preference mode. + +When dark mode is active, Quasar: + +- applies `body--dark` instead of `body--light` +- uses the dark page background and dark-aware component behavior +- automatically enables the dark state of Quasar components that support a `dark` prop + +These behaviors are defined by [Quasar dark mode](https://quasar.dev/style/dark-mode). Application-owned surfaces can key off the same body class and reuse Quasar variables: + +```css +:root { + --app-surface: #ffffff; + --app-text: #202623; +} + +.body--dark { + --app-surface: var(--q-dark); + --app-text: #eef3f0; +} +``` + +Changing `--q-dark` while the page remains in light mode changes consumers of the `dark` role but does not add `body--dark`. Enabling dark mode without designing application-specific text, border, and surface tokens does not automatically recolor arbitrary custom CSS. + +## CSS Classes And Cascade + +NiceGUI ships Quasar's color helpers, so `.classes("text-primary")` and `.classes("bg-warning")` can be attached directly to NiceGUI elements. Quasar defines these helpers with `!important`. + +NiceGUI changes the cascade arrangement around the bundled Quasar CSS. Its [CSS layer reference](https://nicegui.io/documentation/section_styling_appearance#css_layers) explains how Quasar rules are split into layers so important Tailwind utilities or application rules in suitable layers can override them. This is a NiceGUI integration detail; the class names and color semantics still come from Quasar. + +Direct CSS can consume the same semantic properties without a Quasar class: + +```css +.app-focus-ring { + outline: 2px solid var(--q-primary); +} +``` + +Such CSS follows runtime palette changes because it reads the same property. A literal declaration such as `outline-color: #176b5b` does not. + +## Source Index + +!!! info "Primary sources" + - [NiceGUI styling and appearance](https://nicegui.io/documentation/section_styling_appearance) + - [NiceGUI color theming](https://nicegui.io/documentation/colors) + - [NiceGUI dark mode](https://nicegui.io/documentation/dark_mode) + - [Quasar color palette](https://quasar.dev/style/color-palette) + - [Quasar dark mode](https://quasar.dev/style/dark-mode) + - [Quasar theme builder](https://quasar.dev/style/theme-builder) + +!!! info "Implementation references" + - [NiceGUI app-wide color configuration](https://github.com/zauberzeug/nicegui/blob/main/nicegui/app/app.py) + - [NiceGUI page color element](https://github.com/zauberzeug/nicegui/blob/main/nicegui/elements/colors.py) + - [NiceGUI page color client component](https://github.com/zauberzeug/nicegui/blob/main/nicegui/elements/colors.js) + - [NiceGUI custom color CSS generation](https://github.com/zauberzeug/nicegui/blob/main/nicegui/static/nicegui.js) + - [NiceGUI element color classification](https://github.com/zauberzeug/nicegui/blob/main/nicegui/elements/mixins/color_elements.py) + - [NiceGUI color behavior tests](https://github.com/zauberzeug/nicegui/blob/main/tests/test_colors.py) diff --git a/src/personal_mcp/docs/skills/nicegui/references/component-mechanics.md b/src/personal_mcp/docs/skills/nicegui/references/component-mechanics.md index a58a834..c70ffd9 100644 --- a/src/personal_mcp/docs/skills/nicegui/references/component-mechanics.md +++ b/src/personal_mcp/docs/skills/nicegui/references/component-mechanics.md @@ -2,7 +2,7 @@ NiceGUI components are Python objects that describe browser UI elements. A component constructor creates an element, constructor arguments configure its common behavior, and methods on the returned object expose styling, events, bindings, slots, and client-side capabilities. -This reference begins with those everyday component APIs, then describes the NiceGUI, Quasar, Vue, and browser layers beneath them. Themes, responsive composition, and broader visual design are covered separately in [visual styling and CSS](./styling-and-customization.md). +This reference begins with those everyday component APIs, then describes the NiceGUI, Quasar, Vue, and browser layers beneath them. Page structure, typography, responsive composition, and scaling are covered separately in [styling and customization](./styling-and-customization.md). ## Basic Components diff --git a/src/personal_mcp/docs/skills/nicegui/references/styling-and-customization.md b/src/personal_mcp/docs/skills/nicegui/references/styling-and-customization.md index 4aad18e..7136ce6 100644 --- a/src/personal_mcp/docs/skills/nicegui/references/styling-and-customization.md +++ b/src/personal_mcp/docs/skills/nicegui/references/styling-and-customization.md @@ -1,172 +1,71 @@ -# NiceGUI Visual Styling And CSS +# NiceGUI Page Structure, Typography, And Scaling -Use this reference for cosmetic and presentational work: themes, color roles, utility classes, CSS properties, responsive layout, and static assets. For the mechanics of how a NiceGUI Python element maps to a Quasar Vue component, including props, events, slots, methods, teleported content, and wrapper-owned state, load [component mechanics](./component-mechanics.md). +Use this reference for the physical structure of a NiceGUI page: container geometry, Tailwind layout classes, spacing, overflow, responsive reflow, font loading, typography, and scale. Prefer NiceGUI's Python mechanics or Tailwind classes wherever they can express the requirement; custom CSS is the fallback, not a parallel styling path. For the mechanics of how a NiceGUI Python element maps to a Quasar Vue component, including props, events, slots, methods, teleported content, and wrapper-owned state, load [component mechanics](./component-mechanics.md). For package boundaries, dependency direction, and page or component ownership, load [application architecture](./architecture.md). -## Visual Styling Boundary +## Page Structure Boundary -This page owns how an element looks and fits into a page after the correct component and behavior have been chosen. Typical concerns include: +This page owns how elements occupy and share space after the correct components and behavior have been chosen. Typical concerns include: -- application color roles and light or dark presentation -- width, height, spacing, alignment, wrapping, and overflow -- typography, borders, shadows, focus treatments, and state colors +- page shells, content-width constraints, columns, rows, and grid tracks +- width, height, spacing, alignment, wrapping, overflow, and scroll ownership +- font resources, font families, type sizes, weights, line height, and line length - responsive page composition and stable control dimensions -- reusable application classes, CSS custom properties, and static assets +- rem-based sizing, browser text enlargement, and explicit element scaling +- exceptional CSS that cannot be expressed through Python mechanics or Tailwind classes The companion [component mechanics](./component-mechanics.md) reference owns how behavior crosses framework boundaries. Use it when the question is whether a value belongs in a constructor, Quasar prop, Vue event, slot, method, binding, or teleported popup. -## Visual Styling Workflow +## Precedence: Python, Then Tailwind, Then CSS -Escalate only as far as the visual requirement needs: +Apply this order to every structural requirement: -1. Use a NiceGUI constructor argument when it directly expresses appearance, such as an icon, color, or size. -2. Use documented Quasar appearance props through `.props(...)` for component variants such as `outlined`, `rounded`, or `dense`. -3. Use Tailwind classes for page structure and common visual utilities. -4. Use Quasar utility classes for Quasar spacing, typography, semantic colors, visibility, and positioning. -5. Use `.style(...)` for a calculated runtime value or a short-lived visual probe. -6. Move stable or repeated declarations into a scoped static stylesheet under an application-owned class. +1. Use NiceGUI's Python composition and component APIs: containers such as `ui.row`, `ui.column`, and `ui.grid`, constructor arguments, documented properties, slots, and wrapper methods. +2. Add Tailwind classes through `.classes(...)` for width, tracks, spacing, alignment, wrapping, overflow, responsive changes, typography, and other physical presentation. +3. Use Quasar props or helper classes when the requirement belongs specifically to a Quasar component and NiceGUI exposes that boundary. +4. Use `.style(...)` only for a calculated runtime value that cannot be represented by the available APIs or utility classes. +5. Add scoped static CSS only when all preceding layers cannot express the requirement without relying on unsupported component internals. -Stop when the required presentation is achieved. If a proposed rule needs selectors such as `.q-field__control`, changes a popup's mounting or positioning behavior, or depends on generated Vue markup, resolve the component mechanics first instead of compensating with CSS. +Do not create a stylesheet merely to rename or group utilities that fit cleanly in `.classes(...)`. Reuse a Python component or helper when a class sequence repeats. Before adding CSS, identify the unsupported requirement it solves; if the rule needs selectors such as `.q-field__control`, changes popup positioning, or depends on generated Vue markup, resolve the component mechanics first instead of compensating with CSS. ```python -ui.select( - options=items, - label="Item", -).props( - "outlined popup-content-class=app-item-menu" -).classes( - "app-item-select w-full md:max-w-md" +( + ui.select(options=items, label="Item") + .props("outlined") + .classes("w-full md:max-w-md rounded") ) ``` -```css -.app-item-select { - border-radius: 0.25rem; -} +## Physical Layout Model -.app-item-menu { - max-height: min(24rem, 60dvh); -} -``` +Four layout decisions control most NiceGUI page structure: -## Application Themes With NiceGUI And Quasar +| Decision | Typical declarations | Failure when omitted | +| --- | --- | --- | +| outer constraint | `w-full`, `max-w-*`, `mx-auto`, `px-*` | content touches viewport edges or becomes unreadably wide | +| track sizing | `flex-1`, `shrink-0`, `grid-cols-*`, `minmax(0, 1fr)` | sidebars collapse or content forces tracks wider than the viewport | +| intrinsic minimums | `min-w-0`, `min-h-0` | flexible children refuse to shrink and create page-level overflow | +| overflow owner | `overflow-auto`, `overflow-x-auto`, `overflow-hidden` | multiple nested scrollers or clipped interactive content | -Treat a theme as three related layers with different owners: - -1. Configure Quasar's named color roles through NiceGUI. -2. Let Quasar own light, dark, and automatic mode state. -3. Define application semantic tokens for surfaces and content not covered by Quasar components. - -Do not implement a parallel theme switch by replacing Quasar classes or directly restyling each component. NiceGUI's color APIs set the supported Quasar `--q-*` custom properties, so Quasar components, `color=` arguments, and classes such as `text-primary` and `bg-positive` stay aligned. - -### Set The App-Wide Palette Once - -Use [`app.colors()`](https://nicegui.io/documentation/colors#app-wide-colors) in the composition layer for the default palette. Prefer Quasar's semantic roles over shade names: `primary`, `secondary`, `accent`, `positive`, `negative`, `info`, and `warning`. The `dark` and `dark_page` arguments configure dark surface colors; they do not enable dark mode. - -```python -from nicegui import app, ui - -app.colors( - primary="#176b5b", - secondary="#52645f", - accent="#c05a32", - dark="#202523", - dark_page="#151917", - positive="#2e7d32", - negative="#b3261e", - info="#276b8e", - warning="#a86600", - brand="#176b5b", -) - - -@ui.page("/") -def index() -> None: - ui.button("Save") - ui.label("Current workspace").classes("text-brand") - - -ui.run() -``` - -Custom names such as `brand` become Quasar color names and can be used through `color="brand"`, `text-brand`, or `bg-brand`. Register them before any component uses them. `app.colors()` was added in NiceGUI 3.6.0; for an older pinned version, centralize the same `ui.colors(...)` call in a shared page shell. - -Use [`ui.colors()`](https://nicegui.io/documentation/colors) only when one page intentionally overrides the app palette. It is page-scoped and takes precedence over `app.colors()`: - -```python -@ui.page("/operations") -def operations_page() -> None: - ui.colors(primary="#8f3d2c") - ui.button("Operations action") -``` - -Avoid scattering `ui.colors()` calls among reusable components. A component should consume semantic roles from its owning page rather than silently changing the palette for the whole page. - -### Let Quasar Control Light And Dark Mode - -Use [`ui.dark_mode()`](https://nicegui.io/documentation/dark_mode) for page mode. Its value is tri-state: `True` enables dark mode, `False` disables it, and `None` follows the client's `prefers-color-scheme` setting. It overrides the `dark` default supplied to `ui.run()` or `@ui.page` for that page. - -```python -dark_mode = ui.dark_mode(None) - -with ui.button_group(): - ui.button("System", on_click=dark_mode.auto) - ui.button("Light", on_click=dark_mode.disable) - ui.button("Dark", on_click=dark_mode.enable) -``` - -Quasar applies `body--light` or `body--dark`, updates its dark-aware components, and tracks system changes while mode is automatic. Use the NiceGUI element instead of invoking Quasar's JavaScript Dark plugin directly. Persist an explicit user preference separately when it must survive navigation or a new browser session. - -### Add Semantic Tokens For Application Surfaces - -Quasar's brand roles cover framework components, not every application-specific surface. Define a small set of semantic CSS variables in the static stylesheet and change their values under Quasar's documented `.body--dark` class: - -```css -:root { - --app-page: #f6f8f7; - --app-surface: #ffffff; - --app-text: #202623; - --app-border: #cbd4d0; -} - -.body--dark { - --app-page: var(--q-dark-page); - --app-surface: var(--q-dark); - --app-text: #eef3f0; - --app-border: #46504b; -} - -body { - background: var(--app-page); - color: var(--app-text); -} - -.app-panel { - background: var(--app-surface); - border: 1px solid var(--app-border); -} -``` - -Name tokens by purpose, such as `--app-surface` or `--app-muted-text`, rather than by a fixed color such as `--app-gray-100`. Reuse `--q-primary` and the other Quasar variables when the meaning matches. Check text, icon, border, focus, hover, disabled, positive, warning, and negative contrast in both modes; a palette is not complete merely because the page background changes. +NiceGUI rows and columns provide component structure, while their `.classes(...)` values define the physical constraints. Prefer explicit Tailwind `p-*` and `gap-*` classes for local container spacing. NiceGUI's `--nicegui-default-padding` and `--nicegui-default-gap` variables, both `1rem` by default, are CSS-level exceptions for changing the framework-wide baseline rather than one container. ## Structural Styling With Tailwind -Use standard [Tailwind utility classes](https://tailwindcss.com/docs/utility-first) for page and component structure: +NiceGUI's `.classes()` method attaches Tailwind-compatible classes directly to the rendered element. The structural categories used most often are: -- display, flex, and grid behavior -- width, height, and maximum-width constraints -- spacing, gaps, padding, and alignment -- wrapping, overflow, and responsive variants -- typography and common visual utilities when they fully express the design +| Concern | Representative classes | +| --- | --- | +| display and tracks | `flex`, `grid`, `grid-cols-1`, `md:grid-cols-2` | +| growth and shrinkage | `flex-1`, `grow`, `shrink-0`, `basis-*` | +| dimensions | `w-full`, `h-full`, `min-w-0`, `max-w-6xl`, `size-10` | +| spacing | `gap-4`, `px-4`, `py-6`, `mx-auto`, `space-y-3` | +| alignment | `items-start`, `items-center`, `justify-between`, `self-stretch` | +| wrapping and overflow | `flex-wrap`, `whitespace-nowrap`, `overflow-auto`, `truncate` | +| positioning | `relative`, `absolute`, `sticky`, `inset-*`, `z-*` | +| responsive changes | `md:flex-row`, `lg:grid-cols-3`, `xl:max-w-7xl` | -Build the outer layout before fine-tuning individual controls: - -1. Define the page shell and width constraints. -2. Establish responsive rows, columns, gaps, and wrapping. -3. Add semantic sections and repeated visual patterns. -4. Configure component appearance and behavior with constructor arguments and Quasar props. -5. Add stable application classes for any remaining stylesheet rules. +The [Tailwind width](https://tailwindcss.com/docs/width) and [maximum-width](https://tailwindcss.com/docs/max-width) references distinguish fixed spacing-scale widths, fractions, viewport units, and container-scale constraints. A centered shell normally combines its responsibilities explicitly: ```python with ui.column().classes("w-full max-w-6xl mx-auto gap-6 px-4"): @@ -177,60 +76,113 @@ with ui.column().classes("w-full max-w-6xl mx-auto gap-6 px-4"): item_grid().classes("w-full flex-1 min-w-0") ``` -Use stable width, minimum-width, and flex constraints so labels, icons, validation messages, and loaded content do not shift the surrounding layout. +`w-full` fills available inline space, `max-w-6xl` caps line and panel length, `mx-auto` centers the shell, and `px-4` retains edge space below the cap. Inside the row, `shrink-0` protects the sidebar and `min-w-0` allows the flexible content track to become narrower than its intrinsic content. + +Tailwind's [responsive variants](https://tailwindcss.com/docs/responsive-design) are mobile-first. Unprefixed classes apply at every size; `md:*` and larger prefixes apply from their minimum width upward. In NiceGUI's default Tailwind setup, verify available classes against the framework version bundled by the installed NiceGUI release. Optional [UnoCSS presets](https://nicegui.io/documentation/section_styling_appearance#unocss_engine) are intentionally not fully compatible with Tailwind, and Tailwind CSS layers are one documented difference. ### Combine Tailwind With Quasar Utilities Deliberately -NiceGUI's `.classes()` accepts both Tailwind utilities and the CSS helpers bundled with Quasar. Keep Tailwind as the default for application layout and responsive structure, but use Quasar utilities when they express a Quasar-owned or framework-semantic concern more directly: +NiceGUI's `.classes()` accepts both Tailwind utilities and CSS helpers bundled with Quasar. Tailwind remains the default for application layout and responsive structure; Quasar helpers are useful when dimensions should follow Quasar's component conventions: - [`q-m*` and `q-p*` spacing classes](https://quasar.dev/style/spacing) when spacing should follow Quasar's component scale - [typography helpers](https://quasar.dev/style/typography), such as `text-h6`, `text-subtitle2`, and `text-weight-medium`, for text that should follow Quasar's type system -- [color palette classes](https://quasar.dev/style/color-palette), such as `text-primary`, `bg-positive`, and `text-negative`, so semantic colors track the palette configured by `app.colors()` or `ui.colors()` - [visibility helpers](https://quasar.dev/style/visibility), such as `gt-sm` and `lt-md`, when visibility should use Quasar's configured breakpoints - [positioning helpers](https://quasar.dev/style/positioning), such as `absolute-top-right`, when positioning content relative to a Quasar component +- [size and overflow helpers](https://quasar.dev/style/other-helper-classes), such as `fit`, `full-width`, and `overflow-auto`, when matching Quasar layout behavior -Mix the two systems by concern, not by writing competing declarations for the same CSS property. For example, `w-full q-pa-md text-primary` uses Tailwind for width and Quasar for component-scale padding and semantic color. Do not combine `p-4` with `q-pa-md`, or Tailwind and Quasar visibility helpers, on the same element; their cascade order can make the result version-dependent and difficult to review. +Do not assign the same property through both systems on one element. For example, `w-full q-pa-md` uses Tailwind for width and Quasar for component-scale padding; adding `p-4` would create competing padding declarations. The same rule applies to Tailwind and Quasar visibility helpers or to Tailwind font sizes and Quasar heading classes. ```python with ui.card().classes("w-full max-w-2xl q-pa-md"): - ui.label("Inventory summary").classes("text-h6 text-primary") - ui.label("Review required").classes("text-negative text-weight-medium") + ui.label("Inventory summary").classes("text-h6") + ui.label("12 locations").classes("text-subtitle2 text-weight-medium") ``` -Quasar utilities are global classes, so they need no Vue-specific translation before being passed to `.classes()`. Confirm the available helpers and breakpoints against the Quasar version bundled by the installed NiceGUI release. +Tailwind and Quasar do not share breakpoint thresholds. Tailwind's defaults begin `sm` at `40rem` and `md` at `48rem`; Quasar defines `sm` from `600px` and `md` from `1024px`. Keep one breakpoint system responsible for a given layout transition, and confirm the bundled framework versions before relying on exact thresholds. -## Fine Tuning With Static Stylesheets +## CSS As A Last Resort -Move stable fine tuning into a static stylesheet after the structure and native component configuration are correct. Static stylesheets provide reusable selectors, media queries, pseudo-classes, CSS variables, and a clear cascade that inline declarations cannot provide. - -Attach an application-owned class with `.classes()` or a Quasar popup prop, then scope stylesheet rules beneath it: +Do not move stable geometry into a stylesheet simply because a Tailwind class string is long. Tailwind arbitrary values can express constraints such as `minmax(...)`, `min(...)`, aspect ratios, and dynamic viewport units while keeping the rule visible beside the Python structure that owns it. ```python -ui.select(...).props("popup-content-class=app-item-menu").classes( - "app-item-select w-full md:max-w-md" -) +with ui.element("main").classes( + "grid min-h-0 " + "grid-cols-[minmax(14rem,20rem)_minmax(0,1fr)]" +): + sidebar() + workspace().classes("min-w-0") + +ui.select(...).props( + 'outlined popup-content-class="max-h-[min(24rem,60dvh)] overflow-y-auto"' +).classes("w-full md:max-w-md") ``` -```css -.app-item-select { - --app-field-accent: #176b5b; -} +Use `.style()` only when a value is calculated at runtime and no class or component property can represent it. Keep the override on the narrowest element and do not promote it to a shared stylesheet unless it becomes a genuine cross-component rule. -.app-item-select:focus-within { - filter: drop-shadow(0 0 0.25rem rgb(23 107 91 / 20%)); -} - -.app-item-menu { - max-height: min(24rem, 60dvh); -} -``` - -Use `.style()` when a value is calculated at runtime or while testing a local hypothesis. Once a declaration becomes stable or repeated, move it to the stylesheet and keep only the application class in Python. +Static CSS remains appropriate for browser-level facilities such as `@font-face`, selectors or pseudo-elements with no available utility, and integration with markup that cannot receive classes. Attach an application-owned class through `.classes()` or a documented Quasar prop, then scope the exceptional rule beneath that class. Avoid overriding Quasar internals such as `.q-field__label`, `.q-field__native`, `.q-field__control`, and `.q-field__input` unless the public props, slots, and application-level selectors cannot express the requirement. Quasar coordinates field height, padding, labels, values, icons, and floating-label transforms. Changing only one internal part tends to cause clipping or overlap. +## Fonts And Typography + +Typography affects physical layout because font metrics determine line breaks, control height, baseline alignment, and the intrinsic width of labels. Treat font loading and the type scale as structural dependencies rather than late decoration. + +### Font Families And Loading + +Tailwind provides `font-sans`, `font-serif`, and `font-mono`, and supports custom family utilities as documented by [Tailwind font family](https://tailwindcss.com/docs/font-family). Quasar's [typography reference](https://quasar.dev/style/typography) documents its embedded Roboto default and its heading, weight, alignment, wrapping, and case helpers. + +For an application-owned typeface, `@font-face` is one of the browser-level cases that warrants CSS. Mount the font with other static assets and declare it once in the shared stylesheet. [MDN `@font-face`](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face) recommends WOFF2 for modern web delivery; `font-display: swap` keeps text available while the resource loads. + +```css +@font-face { + font-family: "App Sans"; + src: url("/static/fonts/app-sans.woff2") format("woff2"); + font-display: swap; + font-style: normal; + font-weight: 400 700; +} + +.app-shell { + font-family: "App Sans", sans-serif; +} +``` + +Include the real weight range supplied by the font file. Requesting an unavailable weight makes the browser synthesize it and can alter text width. Keep a fallback family so failed or delayed font requests do not leave text unavailable. + +### Type Size And Line Height + +Tailwind's [font-size utilities](https://tailwindcss.com/docs/font-size) pair named rem-based sizes such as `text-sm`, `text-base`, and `text-xl` with default line heights. Combined forms such as `text-sm/6` set size and line height together. Separate `leading-*`, `font-*`, and text-alignment utilities refine those dimensions. + +```python +with ui.column().classes("w-full max-w-[65ch] gap-3"): + ui.label("Inventory summary").classes("text-2xl/8 font-semibold") + ui.label("Counts by location and storage area").classes("text-base/7") +``` + +Prefer a small named hierarchy over unrelated one-off sizes. Use `rem`-based utilities so browser font preferences and page zoom remain meaningful, and use a character-based maximum width such as `max-w-[65ch]` for long prose. Avoid viewport-width font sizing: text should reflow at narrow widths rather than shrink to preserve one line. + +`em` dimensions inherit and can compound through nested elements; `rem` dimensions refer to the root element and avoid that compounding. The [MDN font-size reference](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size) describes both behaviors and recommends relative sizing for accessibility. + +## Scaling Boundaries + +The word "scale" can refer to different browser mechanics. They are not interchangeable: + +| Mechanism | Participates in layout | Appropriate use | +| --- | --- | --- | +| responsive classes and reflow | yes | normal page adaptation across available widths | +| relative font and spacing units | yes | coherent type and spacing changes that respect browser settings | +| browser zoom | yes, at the document level | user-controlled magnification that the page must tolerate | +| CSS `zoom` | yes | exceptional magnification of a bounded region | +| `transform: scale(...)` | no | transient visual emphasis or a deliberately overlaid preview | + +Responsive reflow through Python composition and Tailwind classes is the default for page structure. A narrower page should stack tracks, wrap controls, and retain readable text rather than shrink the entire interface. + +[CSS `zoom`](https://developer.mozilla.org/en-US/docs/Web/CSS/zoom) changes the size used by layout, so surrounding content is recalculated. [`transform: scale()`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale) changes only painting; neighboring elements retain the unscaled geometry, and enlarged content can overlap or overflow its box. Treat both as exceptional effects after responsive widths, gaps, and breakpoints have been exhausted. + +Stable fixed-format regions such as boards, diagrams, and previews need an explicit box before their contents scale. Combine `aspect-ratio`, a bounded inline size, and local overflow rules so transformed content cannot resize surrounding controls. Scaling animations should respect `prefers-reduced-motion`. + ## Responsive Layout Support these layouts only: @@ -239,7 +191,7 @@ Support these layouts only: - landscape desktop: $1920 \times 1080$ with side-by-side panels where they improve scanning - portrait desktop: $1080 \times 1920$ with stacked panels or a narrow fixed sidebar -Build the mobile layout first, then add one desktop breakpoint when a row or grid needs more space. Prefer flex wrapping and fluid grids before adding another breakpoint. Use Tailwind classes for page layout and Quasar props for component behavior. +Build the mobile layout first, then add one desktop breakpoint when a row or grid needs more space. Unprefixed Tailwind classes define the mobile baseline; breakpoint-prefixed classes alter it at larger widths. Prefer flex wrapping and fluid grids before adding another breakpoint. Use Tailwind classes for page layout and Quasar props for component density and behavior. ```python with ui.row().classes("w-full flex-wrap gap-4 lg:flex-nowrap items-start"): @@ -249,12 +201,15 @@ with ui.row().classes("w-full flex-wrap gap-4 lg:flex-nowrap items-start"): Use `min-w-0` for flexible children, `flex-wrap` for toolbars, and `max-w-* mx-auto` to keep portrait layouts readable. Do not add device-specific component trees, container queries, or custom breakpoints unless a supported layout demonstrates a concrete failure. -## Loading Stylesheets And Static Assets +Height needs an explicit ownership chain. `h-full` only resolves when the containing block has a definite height; viewport-bound workspaces usually need a defined outer height and `min-h-0` on nested flex or grid tracks before an inner `overflow-auto` region can scroll. Prefer dynamic viewport units such as `dvh` for browser UI that changes the visible mobile viewport. -- Mount and link static stylesheets once from the composition layer rather than injecting CSS from individual pages. -- Keep custom CSS tokenized with variables and scoped to application classes. +## Loading Exceptional CSS And Static Assets + +- Keep ordinary layout and typography in Python mechanics and Tailwind classes rather than creating a stylesheet. +- When exceptional CSS is required, mount and link it once from the composition layer rather than injecting it from individual pages. +- Keep custom dimensions and font families in named variables or application classes. - Avoid broad rules against Quasar internals. -- Mount referenced assets in the composition layer. +- Mount referenced stylesheets, fonts, and other assets in the composition layer. - Verify mount paths, reverse-proxy rewrites, and cache behavior. ```python @@ -283,23 +238,29 @@ Check each completed page at these three viewports: 2. Landscape desktop at $1920 \times 1080$. 3. Portrait desktop at $1080 \times 1920$. -Confirm that page sections do not overlap, toolbars wrap on mobile, desktop panels use the available space without becoming excessively wide, and dialogs remain visible and scroll to their final field. +Confirm that page sections do not overlap, toolbars wrap on mobile, desktop panels use the available space without becoming excessively wide, and dialogs remain visible and scroll to their final field. Repeat the checks with browser zoom or text enlargement, a delayed font request, long labels, validation messages, and loaded content. Watch for unexpected page-level horizontal scrolling, nested scroll regions, clipped focus outlines, and layout shifts when the webfont replaces its fallback. ## Sources !!! info "Primary sources" - [NiceGUI element styling and props](https://nicegui.io/documentation/element) - [NiceGUI binding properties](https://nicegui.io/documentation/section_binding_properties) - - [NiceGUI color theming](https://nicegui.io/documentation/colors) - - [NiceGUI dark mode](https://nicegui.io/documentation/dark_mode) + - [NiceGUI styling and appearance](https://nicegui.io/documentation/section_styling_appearance) - [Quasar components](https://quasar.dev/vue-components) - [Quasar spacing classes](https://quasar.dev/style/spacing) - [Quasar typography helpers](https://quasar.dev/style/typography) + - [Quasar breakpoints](https://quasar.dev/style/breakpoints) - [Quasar visibility helpers](https://quasar.dev/style/visibility) - [Quasar positioning helpers](https://quasar.dev/style/positioning) - - [Quasar color palette and runtime brand variables](https://quasar.dev/style/color-palette) - - [Quasar dark mode](https://quasar.dev/style/dark-mode) + - [Quasar size and overflow helpers](https://quasar.dev/style/other-helper-classes) - [Quasar field](https://quasar.dev/vue-components/field/) - [Quasar select](https://quasar.dev/vue-components/select/) + - [Tailwind width utilities](https://tailwindcss.com/docs/width) + - [Tailwind maximum-width utilities](https://tailwindcss.com/docs/max-width) + - [Tailwind font-family utilities](https://tailwindcss.com/docs/font-family) + - [Tailwind font-size utilities](https://tailwindcss.com/docs/font-size) - [Tailwind responsive design](https://tailwindcss.com/docs/responsive-design) - - [MDN `zoom`](https://developer.mozilla.org/en-US/docs/Web/CSS/zoom) \ No newline at end of file + - [MDN `@font-face`](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face) + - [MDN `font-size`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size) + - [MDN `zoom`](https://developer.mozilla.org/en-US/docs/Web/CSS/zoom) + - [MDN `scale()`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale) \ No newline at end of file