2025, Oct 17 22:16

Индивидуальная окраска слайдеров в Shiny for Python с CSS

Покажем, как задать отдельные цвета слайдерам в Shiny for Python: даём им уникальные классы через Tag.add_class и нацеливаемся точными CSS‑селекторами.

Покрасить каждый слайдер в Shiny for Python по отдельности кажется простым, пока не пытаешься задать стиль одному элементу, не затрагивая остальные. Для чекбоксов и радиокнопок кастомный CSS обычно срабатывает, но у слайдеров есть своя DOM-структура, требующая точных селекторов. Ниже — короткий и чистый способ оформить каждый слайдер отдельно, без костылей.

Постановка задачи

Три слайдера с одинаковым дефолтным оформлением — по цвету их не различить. Наша цель — придать каждому свой цвет.

from shiny import App, Inputs, Outputs, Session, ui
ui_shell = ui.page_fluid(
    ui.input_slider("r_id", "red", value=0, min=0, max=50),
    ui.input_slider("g_id", "green", value=0, min=0, max=50),
    ui.input_slider("b_id", "blue", value=0, min=0, max=50),
)
def logic(input: Inputs, output: Outputs, session: Session):
    pass
application = App(ui_shell, logic)

Почему индивидуальные цвета ползунков не работают «из коробки»

Даже если добавить CSS в приложение, правила, как правило, задевают все слайдеры, потому что у них одинаковая внутренняя разметка. Чтобы стилизовать один контрол и не трогать другие, контейнер каждого слайдера должен получить собственный класс. Тогда селекторы можно направлять вглубь — к метке значения, полосе и бегунку — используя уникальные классы как область видимости.

Решение

Ключ — навесить уникальный класс на каждый слайдер через Tag.add_class(). После этого нацеливаемся на внутренние элементы скоуп‑селекторами в CSS. Ниже — рабочий шаблон: одному слайдеру назначаем зелёную тему, другому — красную.

from shiny import App, Inputs, Outputs, Session, ui
page_body = ui.page_fluid(
    ui.input_slider("hue_red", "red", value=0, min=0, max=50).add_class("toneRed"),
    ui.input_slider("hue_green", "green", value=0, min=0, max=50).add_class("toneGreen"),
    ui.tags.style(
        """
        .toneGreen > .irs.irs--shiny .irs-single { /* метка значения */
            background-color: #90EE90;
            color: black;
        }
        .toneGreen > * > .irs-bar.irs-bar--single { /* полоса */
            background-color: #90EE90;
        }
        .toneGreen > * > .irs-handle.single { /* бегунок */
            background-color: #90EE90;
        }
        .toneRed > .irs.irs--shiny .irs-single { /* метка значения */
            background-color: #FF0000;
            color: black;
        }
        .toneRed > * > .irs-bar.irs-bar--single { /* полоса */
            background-color: #FF0000;
        }
        .toneRed > * > .irs-handle.single { /* бегунок */
            background-color: #FF0000;
        }
        """
    ),
)
def backend(input: Inputs, output: Outputs, session: Session):
    pass
shiny_app = App(page_body, backend)

Что происходит под капотом

Tag.add_class() назначает отличительный класс обёртке слайдера. CSS затем ограничивается этой обёрткой и проходит к внутренним узлам, опираясь на существующую разметку слайдера: метка значения, основная полоса и бегунок адресуются селекторами .irs.irs--shiny .irs-single, .irs-bar.irs-bar--single и .irs-handle.single соответственно. Поскольку каждое правило начинается с уникального класса-обёртки, стили применяются только к нужному слайдеру.

Почему это важно

Когда требуется индивидуальная тема для каждого элемента управления, глобального CSS недостаточно. Скоупинг стилей через отдельные классы предотвращает случайные переопределения, сохраняет визуальную согласованность компонентов и избавляет от хрупких селекторов, которые цепляют всё подряд. Это также упрощает поддержку: чтобы добавить новый цвет, достаточно одного класса и пары правил вместо каскада исключений.

Выводы

Дайте каждому слайдеру свой класс с Tag.add_class(), затем ограничьте область действия CSS этим классом и адресуйтесь к внутренним элементам слайдера. Такой подход предсказуем, легко повторяется и локализует изменения дизайна. Если позже появятся новые слайдеры или темы, расширяйте ту же структуру, не затрагивая остальной интерфейс.

Статья основана на вопросе с сайта StackOverflow от Redz и ответе от Jan.