Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a stylekit class to make customization easier and accessible #2329

Open
1 of 7 tasks
FlorianJacta opened this issue Dec 13, 2024 · 0 comments
Open
1 of 7 tasks
Labels
💬 Discussion Requires some discussion and decision 🖰 GUI Related to GUI 🆘 Help wanted Open to participation from the community ✨New feature 🟧 Priority: High Must be addressed as soon

Comments

@FlorianJacta
Copy link
Member

FlorianJacta commented Dec 13, 2024

Description

The goal of this issue is to discuss adding a Stylekit class/object/function to make styling easier with the style kit.

Solution Proposed

Would a Stylekit object be useful that provide ways to:

  • see what is inside the stylekit

image

    with tgb.layout(columns="1 1", class_name=sk.layout.align_columns_center):
        with tgb.part(sk.components.sidebar):

image

  • set the theme of your application from a set of predefined theme (that you could change)
sk.set_theme(sk.themes.Coffee)
  • set variables within the Stylekit
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")

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

sk.set_theme(sk.themes.Coffee)

image

image

Industrial

sk.set_theme(sk.themes.Industrial)

image

Monospace

sk.set_theme(sk.themes.Monospace)

image

image

image

Ocean

image

image

POC Code

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 "


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: str):
        self.stylekit["primary_color"] = color

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

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

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

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

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

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

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

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

    def get_stylekit(self):
        return self.stylekit


# Create an instance of Stylekit
stylekit = Stylekit()

Acceptance Criteria

  • If applicable, a new demo code is provided to show the new feature in action.
  • Integration tests exhibiting how the functionality works are added.
  • Any new code is covered by a unit tested.
  • Check code coverage is at least 90%.
  • Related issue(s) in taipy-doc are created for documentation and Release Notes are updated.

Code of Conduct

  • I have checked the existing issues.
  • I am willing to work on this issue (optional)
@FlorianJacta FlorianJacta added 🖰 GUI Related to GUI 🟧 Priority: High Must be addressed as soon ✨New feature 💬 Discussion Requires some discussion and decision labels Dec 13, 2024
@jrobinAV jrobinAV added the 🆘 Help wanted Open to participation from the community label Dec 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💬 Discussion Requires some discussion and decision 🖰 GUI Related to GUI 🆘 Help wanted Open to participation from the community ✨New feature 🟧 Priority: High Must be addressed as soon
Projects
None yet
Development

No branches or pull requests

2 participants