taipy icon indicating copy to clipboard operation
taipy copied to clipboard

[DOCS] Styling needs to be more accessible for Python users

Open AlexandreSajus opened this issue 1 year ago • 3 comments

Issue Description

Most of the support requests we get from Enterprise customers are about styling. They either don't know or don't want to learn how to style in Taipy. A tutorial should clearly explain how to change colors, fonts, sizes, and layouts of visual elements. This tutorial should explain how we style applications by inspecting the page, applying CSS directly in the browser, and then creating a .css file to apply the style.

Code of Conduct

  • [X] I have checked the existing issues.
  • [ ] I am willing to work on this issue (optional)

AlexandreSajus avatar Oct 16 '24 13:10 AlexandreSajus

Is it a duplicate of issue https://github.com/Avaiga/taipy-doc/issues/1095 ?

jrobinAV avatar Oct 16 '24 14:10 jrobinAV

For information, we also have the following issues on the topic: https://github.com/Avaiga/taipy-doc/issues/1113 https://github.com/Avaiga/taipy-doc/issues/1085 https://github.com/Avaiga/taipy-doc/issues/1095 https://github.com/Avaiga/taipy/issues/1077

jrobinAV avatar Oct 16 '24 14:10 jrobinAV

Ah my bad, I though we decided to move all issues to "taipy" repo, I only looked there for duplicates

AlexandreSajus avatar Oct 16 '24 14:10 AlexandreSajus

Would a Stylekit object be useful that provide ways to:

  • see what is inside the stylekit
  • set variables within the Stylekit
  • set the theme of your application from a set of predefined theme (that you could change)

POC:

main.py

from taipy.gui import Gui, notify
import taipy.gui.builder as tgb
from stylekit import stylekit as sk
import pandas as pd
from math import exp, cos

data = pd.DataFrame(
    {
        "Name": ["Florian", "John", "Jane", "Alice", "Bob"],
        "Value": [6, 7, 8, 9, 10],
    }
)
value = 10


def notify_user(state):
    notify(state, "info", "Date is clicked!")


def compute(value):
    return [cos(i / 6) * exp(-i * value / 600) for i in range(100)]


with tgb.Page() as page:
    tgb.text(
        "Hello World from Taipy",
        class_name=sk.typography.h1
        + sk.text_alignment.underline
        + sk.text_alignment.uppercase,
    )
    tgb.text(
        "This is a caption of my chart: we are seeing a sine wave",
        class_name=sk.typography.text_caption,
    )

    with tgb.layout(columns="1 1", class_name=sk.layout.align_columns_center):
        with tgb.part(sk.components.sidebar):
            tgb.chart(lambda value: compute(value))
        with tgb.part(sk.opacity.half_transparent):
            tgb.text("Change the data", class_name=sk.typography.h2)
            with tgb.layout("1 1"):
                tgb.date("2021-01-01", on_change=notify_user, class_name=sk.paddings.p4)
                tgb.selector(
                    "Florian",
                    lov=["Florian", "John", "Jane", "Alice", "Bob"],
                    on_change=notify_user,
                    class_name=sk.paddings.p4,
                    dropdown=True,
                )

            tgb.table("{data}")


sk.set_theme(sk.themes.Coffee)
# sk.set_primary_color("red")
# sk.set_secondary_color("red")
# sk.set_border_radius("10px")
# sk.set_font_family("Arial")
# sk.set_color_background_light("black")
# sk.set_color_background_dark("black")
# sk.set_color_paper_light("grey")
# sk.set_color_paper_dark("white")
# sk.set_input_button_height("40px")

Gui(page).run(stylekit=sk.get_stylekit())

Coffee image

Industrial image

Monospace image

stylekit.py

# stylekit.py


class Components:
    card = "card "
    sidebar = "sidebar "
    # Add other components as needed


class Typography:
    h1 = "h1 "
    h2 = "h2 "
    h3 = "h3 "
    h4 = "h4 "
    h5 = "h5 "
    h6 = "h6 "
    text_body = "text-body "
    text_small = "text-small "
    text_caption = "text-caption "
    # Add other typography styles as needed


class TextWeights:
    weight300 = "text-weight300 "
    weight400 = "text-weight400 "
    weight500 = "text-weight500 "
    weight600 = "text-weight600 "
    weight700 = "text-weight700 "
    weight800 = "text-weight800 "
    weight900 = "text-weight900 "
    # Add other text weights as needed


class TextAlignment:
    left = "text-left "
    center = "text-center "
    right = "text-right "
    uppercase = "text-uppercase "
    no_transform = "text-no-transform "
    underline = "text-underline "
    no_underline = "text-no-underline "
    # Add other text alignments as needed


class Margins:
    """Margins ..."""

    m0 = "m0 "
    m_auto = "m-auto "
    m_half = "m-half "
    m1 = "m1 "
    m2 = "m2 "
    m3 = "m3 "
    m4 = "m4 "
    m5 = "m5 "
    m6 = "m6 "
    mt0 = "mt0 "
    mt_auto = "mt-auto "
    mt_half = "mt-half "
    mt1 = "mt1 "
    mt2 = "mt2 "
    mt3 = "mt3 "
    mt4 = "mt4 "
    mt5 = "mt5 "
    mt6 = "mt6 "
    mb0 = "mb0 "
    mb_auto = "mb-auto "
    mb_half = "mb-half "
    mb1 = "mb1 "
    mb2 = "mb2 "
    mb3 = "mb3 "
    mb4 = "mb4 "
    mb5 = "mb5 "
    mb6 = "mb6 "
    ml0 = "ml0 "
    ml_auto = "ml-auto "
    ml_half = "ml-half "
    ml1 = "ml1 "
    ml2 = "ml2 "
    ml3 = "ml3 "
    ml4 = "ml4 "
    ml5 = "ml5 "
    ml6 = "ml6 "
    mr0 = "mr0 "
    mr_auto = "mr-auto "
    mr_half = "mr-half "
    mr1 = "mr1 "
    mr2 = "mr2 "
    mr3 = "mr3 "
    mr4 = "mr4 "
    mr5 = "mr5 "
    mr6 = "mr6 "
    # Add other margins as needed


class Paddings:
    p0 = "p0 "
    p_half = "p-half "
    p1 = "p1 "
    p2 = "p2 "
    p3 = "p3 "
    p4 = "p4 "
    p5 = "p5 "
    p6 = "p6 "
    pt0 = "pt0 "
    pt_half = "pt-half "
    pt1 = "pt1 "
    pt2 = "pt2 "
    pt3 = "pt3 "
    pt4 = "pt4 "
    pt5 = "pt5 "
    pt6 = "pt6 "
    pb0 = "pb0 "
    pb_half = "pb-half "
    pb1 = "pb1 "
    pb2 = "pb2 "
    pb3 = "pb3 "
    pb4 = "pb4 "
    pb5 = "pb5 "
    pb6 = "pb6 "
    pl0 = "pl0 "
    pl_half = "pl-half "
    pl1 = "pl1 "
    pl2 = "pl2 "
    pl3 = "pl3 "
    pl4 = "pl4 "
    pl5 = "pl5 "
    pl6 = "pl6 "
    pr0 = "pr0 "
    pr_half = "pr-half "
    pr1 = "pr1 "
    pr2 = "pr2 "
    pr3 = "pr3 "
    pr4 = "pr4 "
    pr5 = "pr5 "
    pr6 = "pr6 "
    # Add other paddings as needed


class Visibility:
    d_none = "d-none "
    d_flex = "d-flex "
    d_block = "d-block "
    d_inline = "d-inline "
    d_inline_block = "d-inline-block "
    # Add other visibility classes as needed


class Opacity:
    transparent = "transparent "
    half_transparent = "half-transparent "
    opaque = "opaque "
    # Add other opacity classes as needed


class LayoutModifiers:
    # Layout block modifiers
    align_columns_top = "align-columns-top "
    align_columns_center = "align-columns-center "
    align_columns_bottom = "align-columns-bottom "
    align_columns_stretch = "align-columns-stretch "

    # Layout child part modifiers
    align_item_top = "align-item-top "
    align_item_center = "align-item-center "
    align_item_bottom = "align-item-bottom "
    align_item_stretch = "align-item-stretch "
    # Add other layout modifiers as needed


class Layout:
    taipy_layout = "taipy-layout"
    taipy_part = "taipy-part"
    taipy_dark = "taipy-dark"
    taipy_light = "taipy-light"
    # Add other layout classes as needed


class Theme:
    """Base Theme class."""

    def __init__(
        self,
        name,
        primary_color,
        secondary_color,
        border_radius,
        font_family,
        color_background_light,
        color_background_dark,
        color_paper_light,
        color_paper_dark,
        input_button_height,
    ):
        self.name = name
        self.primary_color = primary_color
        self.secondary_color = secondary_color
        self.border_radius = border_radius
        self.font_family = font_family
        self.color_background_light = color_background_light
        self.color_background_dark = color_background_dark
        self.color_paper_light = color_paper_light
        self.color_paper_dark = color_paper_dark
        self.input_button_height = input_button_height


class Themes:
    """Collection of themes for the Stylekit."""

    Default = Theme(
        name="Default",
        primary_color="#FF462B",
        secondary_color="#283282",
        border_radius=8,
        font_family="Lato, Arial, sans-serif",
        color_background_light="#F0F5F7",
        color_background_dark="#152335",
        color_paper_light="#FFFFFF",
        color_paper_dark="#1F2F44",
        input_button_height="48px",
    )

    #: Monospace Theme: Coding or terminal-like appearance.
    Monospace = Theme(
        name="Monospace",
        primary_color="#333333",
        secondary_color="#555555",
        border_radius="0px",
        font_family="'Courier New', monospace",
        color_background_light="#EFEFEF",
        color_background_dark="#2E2E2E",
        color_paper_light="#FFFFFF",
        color_paper_dark="#3C3C3C",
        input_button_height="44px",
    )

    #: Coffee Theme: Warm, cozy coffee shop atmosphere.
    Coffee = Theme(
        name="Coffee",
        primary_color="#6F4E37",
        secondary_color="#A67B5B",
        border_radius="12px",
        font_family="'Georgia', serif",
        color_background_light="#F5F5DC",
        color_background_dark="#4B3832",
        color_paper_light="#FFFFFF",
        color_paper_dark="#854442",
        input_button_height="50px",
    )

    #: Ocean Theme: Calm and refreshing aquatic feel.
    Ocean = Theme(
        name="Ocean",
        primary_color="#2E8BC0",
        secondary_color="#145DA0",
        border_radius="16px",
        font_family="'Arial', sans-serif",
        color_background_light="#B1D4E0",
        color_background_dark="#133B5C",
        color_paper_light="#FFFFFF",
        color_paper_dark="#1E5F74",
        input_button_height="48px",
    )

    #: Minimalist Theme: Clean and simple design.
    Minimalist = Theme(
        name="Minimalist",
        primary_color="#000000",
        secondary_color="#7F7F7F",
        border_radius="0px",
        font_family="'Helvetica Neue', sans-serif",
        color_background_light="#FFFFFF",
        color_background_dark="#F2F2F2",
        color_paper_light="#FFFFFF",
        color_paper_dark="#E6E6E6",
        input_button_height="46px",
    )

    #: Vibrant Theme: Playful and energetic appearance.
    Vibrant = Theme(
        name="Vibrant",
        primary_color="#FF6F61",
        secondary_color="#6B5B95",
        border_radius="24px",
        font_family="'Comic Sans MS', cursive, sans-serif",
        color_background_light="#FFF0E6",
        color_background_dark="#4A4A4A",
        color_paper_light="#FFFFFF",
        color_paper_dark="#FF6F61",
        input_button_height="52px",
    )

    #: Industrial Theme: Rugged and mechanical aesthetic.
    Industrial = Theme(
        name="Industrial",
        primary_color="#5A5A5A",
        secondary_color="#A1A1A1",
        border_radius="4px",
        font_family="'Roboto', sans-serif",
        color_background_light="#D3D3D3",
        color_background_dark="#2C2C2C",
        color_paper_light="#E0E0E0",
        color_paper_dark="#3A3A3A",
        input_button_height="48px",
    )

    #: Futuristic Theme: Sleek and modern design.
    Futuristic = Theme(
        name="Futuristic",
        primary_color="#00FFFF",
        secondary_color="#FF00FF",
        border_radius="20px",
        font_family="'Orbitron', sans-serif",
        color_background_light="#1A1A1A",
        color_background_dark="#000000",
        color_paper_light="#262626",
        color_paper_dark="#0D0D0D",
        input_button_height="50px",
    )

    #: Classic Theme: Traditional and timeless appearance.
    Classic = Theme(
        name="Classic",
        primary_color="#003366",
        secondary_color="#336699",
        border_radius="8px",
        font_family="'Times New Roman', serif",
        color_background_light="#CCCCCC",
        color_background_dark="#F0F0F0",
        color_paper_light="#CCCCCC",
        color_paper_dark="#E0E0E0",
        input_button_height="48px",
    )

    #: Nature Theme: Earthy and natural tones.
    Nature = Theme(
        name="Nature",
        primary_color="#6B8E23",
        secondary_color="#556B2F",
        border_radius="12px",
        font_family="'Verdana', sans-serif",
        color_background_light="#F5FFFA",
        color_background_dark="#2F4F4F",
        color_paper_light="#FFFFFF",
        color_paper_dark="#8FBC8F",
        input_button_height="48px",
    )

    #: HighContrast Theme: For accessibility and readability.
    HighContrast = Theme(
        name="HighContrast",
        primary_color="#FFFFFF",
        secondary_color="#000000",
        border_radius="0px",
        font_family="'Arial Black', sans-serif",
        color_background_light="#000000",
        color_background_dark="#000000",
        color_paper_light="#FFFFFF",
        color_paper_dark="#FFFFFF",
        input_button_height="48px",
    )


class Stylekit:
    components = Components()
    typography = Typography()
    text_weights = TextWeights()
    text_alignment = TextAlignment()
    margins = Margins()
    paddings = Paddings()
    visibility = Visibility()
    opacity = Opacity()
    layout = LayoutModifiers()
    themes = Themes()
    # Add other style properties as needed

    def __init__(self):
        # Default theme is 'Default'
        self.set_theme(self.themes.Default)

    def set_theme(self, theme):
        if isinstance(theme, Theme):
            self.stylekit = {
                "primary_color": theme.primary_color,
                "secondary_color": theme.secondary_color,
                "border_radius": theme.border_radius,
                "font_family": theme.font_family,
                "color_background_light": theme.color_background_light,
                "color_background_dark": theme.color_background_dark,
                "color_paper_light": theme.color_paper_light,
                "color_paper_dark": theme.color_paper_dark,
                "input_button_height": theme.input_button_height,
            }
        elif isinstance(theme, str):
            theme_obj = getattr(self.themes, theme, None)
            if theme_obj and isinstance(theme_obj, Theme):
                self.set_theme(theme_obj)
            else:
                available_themes = [
                    t
                    for t in dir(self.themes)
                    if not t.startswith("__")
                    and isinstance(getattr(self.themes, t), Theme)
                ]
                print(
                    f"Theme '{theme}' not recognized. Available themes: {', '.join(available_themes)}"
                )
        else:
            raise TypeError(
                "Theme must be a Theme instance or a string representing the theme name."
            )

    # You can add individual setter methods if needed
    def set_primary_color(self, color):
        self.stylekit["primary_color"] = color

    def set_secondary_color(self, color):
        self.stylekit["secondary_color"] = color

    def set_border_radius(self, radius):
        self.stylekit["border_radius"] = radius

    def set_font_family(self, family):
        self.stylekit["font_family"] = family

    def set_color_background_light(self, color):
        self.stylekit["color_background_light"] = color

    def set_color_background_dark(self, color):
        self.stylekit["color_background_dark"] = color

    def set_color_paper_light(self, color):
        self.stylekit["color_paper_light"] = color

    def set_color_paper_dark(self, color):
        self.stylekit["color_paper_dark"] = color

    def set_input_button_height(self, height):
        self.stylekit["input_button_height"] = height

    def get_stylekit(self):
        return self.stylekit


# Create an instance of Stylekit
stylekit = Stylekit()

FlorianJacta avatar Dec 03 '24 15:12 FlorianJacta

Yes this could help

AlexandreSajus avatar Dec 04 '24 00:12 AlexandreSajus