Styling ======= ngapp provides a Python-native styling system that replaces raw CSS strings with composable, reusable objects. The module lives at :mod:`ngapp.style` and exports four classes: :class:`~ngapp.style.Style`, :class:`~ngapp.style.Theme`, :class:`~ngapp.style.CssClass`, and :class:`~ngapp.style.StyleSheet`. .. tip:: **Prefer Quasar utilities for standard layout and spacing.** Quasar already provides classes for padding (``q-pa-md``), margin (``q-mt-sm``), flex layout (``row``, ``col``), visibility (``ui_hidden``), and colors (``bg-primary``, ``text-grey-7``). Use ``Style`` and ``StyleSheet`` for things Quasar doesn't cover: custom overlays, app-specific panel layouts, domain-specific visual treatments, and shared multi-property styles that have no Quasar equivalent. Quick Example ------------- A fixed-position status indicator: .. code-block:: python from ngapp.style import Style, Theme, StyleSheet from ngapp.components import Div theme = Theme( primary="#164d7d", muted="#78909c", border="#e0e0e0", font_sm="0.75rem", spacing=(0, 4, 8, 12, 16, 20, 24, 32), ) css = StyleSheet() # Fixed overlay status_indicator = css.add(Style( position="fixed", bottom="20px", left="50%", transform="translateX(-50%)", background="rgba(15, 23, 42, 0.92)", backdrop_filter="blur(4px)", color="white", padding="8px 20px", border_radius="8px", box_shadow="0 4px 12px rgba(0,0,0,0.3)", z_index=9999, font_size=theme.font_sm, display="flex", align_items="center", gap="8px", )) Div("Solving…", ui_class=status_indicator) css.inject(app) Style — Composable CSS Property Bag ------------------------------------ :class:`~ngapp.style.Style` turns Python keyword arguments into CSS properties. Underscores become hyphens (``font_size`` → ``font-size``). .. code-block:: python from ngapp.style import Style overlay = Style( position="fixed", bottom="20px", background="rgba(0,0,0,0.8)", backdrop_filter="blur(4px)", color="white", z_index=9999, ) **Merging with** ``|``: The ``|`` operator merges two styles. The right side wins on conflicts, matching Python ``dict |`` semantics (PEP 584). Set a value to ``None`` to remove a property. .. code-block:: python panel = Style( border_right="1px solid #e0e0e0", height="100%", overflow_y="auto", ) # Left panel: same but border on the other side panel_left = panel | Style(border_right=None, border_left="1px solid #e0e0e0") **Using with components**: ``Style`` objects work directly with ``ui_style`` — they are converted to strings automatically. Use this for truly dynamic, per-instance values: .. code-block:: python # Dynamic color from a color picker — must be inline swatch.ui_style = Style(background_color=self.to_hex_string()) # Per-instance grid placement cell.ui_style = Style(grid_column=f"{col} / span 2", grid_row=str(row)) .. note:: Integer values are **not** automatically converted to pixels. Write ``padding="8px"`` instead of ``padding=8``. This avoids ambiguity with unitless properties like ``z_index`` and ``font_weight``. Theme — Design Tokens --------------------- :class:`~ngapp.style.Theme` is a simple namespace for centralizing colors, spacing, and font sizes. This avoids scattering magic hex values and pixel numbers across files. .. code-block:: python from ngapp.style import Theme theme = Theme( # Quasar brand colors — applied to the app via theme.apply() primary="#164d7d", secondary="#93B1D4", accent="#14B8A6", positive="#16A34A", negative="#DC2626", info="#0EA5E9", warning="#F59E0B", # App-specific tokens — used in Style() and StyleSheet muted="#78909c", surface="#f5f7fa", border="#e0e0e0", font_sm="0.75rem", font_md="0.85rem", spacing=(0, 4, 8, 12, 16, 20, 24, 32), ) theme.primary # → "#164d7d" theme.font_sm # → "0.75rem" theme.sp(2) # → "8px" (spacing[2]) theme.border_line() # → "1px solid #e0e0e0" **Applying Quasar brand colors:** If the theme has attributes matching Quasar brand color names (``primary``, ``secondary``, ``accent``, ``dark``, ``positive``, ``negative``, ``info``, ``warning``), calling :meth:`~ngapp.style.Theme.apply` sets them on the app. This replaces the manual ``app.set_colors(...)`` call: .. code-block:: python # In your app's __init__: theme.apply(self) # equivalent to self.set_colors(primary="#164d7d", secondary=...) Tokens that are not Quasar brand names (``muted``, ``surface``, ``border``, ``font_sm``, etc.) are ignored by ``apply()`` and are only used in your own ``Style`` definitions. **Helpers:** - ``sp(index)`` — returns ``f"{spacing[index]}px"`` - ``border_line(width, style, color)`` — returns a CSS border shorthand, defaults to ``1px solid {theme.border}`` StyleSheet and CssClass — The Efficiency Layer ----------------------------------------------- Instead of sending full inline CSS strings over the wire for every component, :class:`~ngapp.style.StyleSheet` registers styles as CSS classes and injects a single ``