On this page

StyleKit Features Overview

This page gives a brief introduction to the main features of StyleKit. For a complete reference of all available types and properties, see the QML Types page.

Creating a Style

A Style is a QML object that describes the visual appearance of all Qt Quick Controls in your application — button, slider, checkBox, and so on. Each has its own group in the style where you can set properties such as colors, sizes, radii, and shadows for the visual parts that make up the control.

The control group is special, since it acts as a fallback for all the other control groups. If you leave out some of the properties for a slider, for example, they will be read from control instead. Properties not set in slider or control fall back further to the fallback style which is a complete style similar to the Basic Style. This means you don't necessarily need to style all the available controls, only the ones you want to customize — the fallback system takes care of styling the rest:

// PlainStyle.qml

Style {
    control {
        // control does not map to an actual Qt Quick Control, but is a shared
        // fallback for all other controls. Use it to define styling that is
        // common to all of them. Unset properties fall back to Style.fallbackStyle.
        padding: 6
        text.color: "white"
        background {
            radius: 4
            border.color: "gray"
        }
        indicator {
            implicitWidth: 20
            implicitHeight: 20
            border.width: 1
            radius: 3
        }
        handle {
            implicitWidth: 20
            implicitHeight: 20
            radius: 10
        }
    }

    slider {
        // slider defines the styling for a Qt Quick Slider.
        // Unset properties fall back to control.
        handle.color: "white"
        indicator {
            implicitWidth: Style.Stretch
            implicitHeight: 6
            color: "steelblue"
            foreground.color: "skyblue"
        }
    }

    abstractButton {
        // abstractButton does not map to an actual Qt Quick Control, but
        // is a shared fallback for button-like controls, such as button,
        // radioButton, checkBox). Use it to define styling that is common
        // to all of them. Unset properties fall back to control.
        background.shadow {
            opacity: 0.6
            verticalOffset: 2
            horizontalOffset: 2
            color: "gray"
        }
    }

    button {
        // button defines the styling for a Qt Quick Button.
        // Unset properties fall back to abstractButton.
        background {
            implicitWidth: 120
            color: "lightsteelblue"
            gradient: Gradient {
                GradientStop { position: 0.0; color: Qt.alpha("black", 0.0)}
                GradientStop { position: 1.0; color: Qt.alpha("black", 0.2)}
            }
        }
    }

    // Controls left undefinedsuch as radioButton, checkBox, or
    // switchControl — fall back to their immediate base type, which in
    // this style will be either abstractButton or control directly.
}

Activating a Style

To activate the style, assign it to StyleKit.style on the root ApplicationWindow. All controls in the application then pick it up automatically:

// Main.qml

import QtQuick
import Qt.labs.StyleKit

ApplicationWindow {
    id: app
    width: 1024
    height: 800
    visible: true

    // Assign the style to be used
    StyleKit.style: PlainStyle {}

    // Controls are used as normal
    Column {
        anchors.fill: parent
        anchors.margins: 10
        spacing: 10

        Button {
            text: "Button"
        }

        Slider {
            width: 200
        }
    }
}

Control States

Controls change appearance depending on user interaction — a button looks different when hovered, pressed, or disabled. StyleKit lets you express this in the style by placing the state name in front of the affected properties, to give them alternative values when the control is in that state.

States can be nested, and a more specific combination (e.g. hovered.checked) takes precedence over its individual components:

button {
    text.color: "aliceblue"
    background.color: "cornflowerblue"
    pressed.background.color: "deepskyblue"
    hovered.background.color: "lightskyblue"
    focused.background.color: "lightsteelblue"
    checked.background.color: "royalblue"
    highlighted.background.color: "lightblue"
    disabled {
        background.color: "lightgray"
        background.border.color: "darkgray"
    }

    // Nested states, such as hovered.checked in this case, takes
    // precedence over both hovered and checked:
    hovered.checked.background.color: "steelblue"
}

State Transitions

State changes can be animated by setting the transition property on a control style. A StyleAnimation provides a convenient way to animate groups of related style properties, such as all background or indicator colors, but you can use standard QML animations such as ColorAnimation and NumberAnimation as well:

comboBox {
    background.color: "lightgray"
    hovered.background.color: "plum"

    indicator.color: "white"
    hovered.indicator.color: "pink"
    hovered.indicator.border.width: 4

    transition: Transition {
        StyleAnimation {
            animateBackgroundColors: true
            animateIndicatorColors: true
            animateIndicatorBorder: true
            easing.type: Easing.OutQuad
            duration: 500
        }
    }
}

Theming

StyleKit has built-in support for light and dark themes through the light and dark properties on a Style. Similar to a Style, a Theme lets you define the style each control should have when that theme is active. Properties that are not set in a theme will fall back to be read from the style:

Style {
    light: Theme {
        applicationWindow.background.color: "gainsboro"
        control.text.color: "#202020"
        control.background.color: "#f0f0f0"
        control.background.border.color: "#d0d0d0"
        button.hovered.background.color: "#4a90d9"
        radioButton.indicator.foreground.color: "#d0d0d0"
    }

    dark: Theme {
        applicationWindow.background.color: "#2b2b2b"
        control.text.color: "#e0e0e0"
        control.background.color: "#404040"
        control.background.border.color: "#606060"
        button.hovered.background.color: "#6ab0f9"
        radioButton.indicator.foreground.color: "#606060"
    }
}

Custom Themes

Beyond light and dark, you can define any number of additional themes using CustomTheme. Each CustomTheme has a name and holds a Theme object with the same structure as the built-in themes:

Style {
    CustomTheme {
        name: "HighContrast"
        theme: Theme {
            control.background.color: "white"
            control.background.border.color: "black"
            control.background.border.width: 2
        }
    }

    CustomTheme {
        name: "Sepia"
        theme: Theme {
            control.text.color: "#5b4636"
            control.background.color: "#f4ecd8"
            control.background.border.color: "#c8b99a"
            applicationWindow.background.color: "#efe6d0"
        }
    }
}

To switch themes at runtime, set Style.themeName from a QML file in your application to the name of the desired theme. The Style.themeNames property lists all available theme names, which makes it straightforward to populate a selector control:

ComboBox {
    model: StyleKit.style.themeNames
    onCurrentTextChanged: StyleKit.style.themeName = currentText
}

To activate a theme at application start-up, set themeName when assigning the style:

ApplicationWindow {
    width: 1024
    height: 800
    visible: true

    StyleKit.style: MyStyleKitStyle {
        themeName: "HighContrast"
    }
}

Style Variations

A StyleVariation lets you define alternative styling for parts of the application. This is useful when you need to style controls differently when they are children of, for example, a ToolBar or a GroupBox, or if you want to implement style hints that the application can optionally apply to some of the controls.

There are two types of style variations: type variations and instance variations.

Type Variations

A type variation contains alternative styling for controls that are children (or descendants) of another control type. This means that if a StyleVariation contains styling for a button, and it's added to the variations property of a frame, StyleKit will style all Buttons that are children of Frames in the application accordingly:

Style {
    frame {
        variations: StyleVariation {
            button {
                text.color: "ghostwhite"
                background.border.width: 0
                background.color: "slategrey"
            }
        }
    }

    groupBox {
        // groupBox falls back to frame. Therefore, if the varations set on a
        // frame is not wanted on a groupBox, just override it and set it back to [].
        variations: []
    }
}

Instance Variations

Named StyleVariations can be applied to individual controls in the application using the StyleVariation.variations attached property. When applied, the control itself, and all its descendants, will receive the alternative styling. This is different from a type variation, which affects all controls of a certain type, such as all Frames. Instance variations will only affect the control instance (and the descendants) on which they're attached:

Style {
    StyleVariation {
        name: "mini"
        control {
            padding: 2
            background.implicitHeight: 15
            indicator.implicitWidth: 15
            indicator.implicitHeight: 15
            handle.implicitWidth: 15
            handle.implicitHeight: 15
        }
    }

    StyleVariation {
        name: "alert"
        abstractButton.background.color: "red"
    }
}

Apply them to controls in your application:

GroupBox {
    title: "Mini controls"
    StyleVariation.variations: ["mini"]

    Row {
        spacing: 10
        Button { text: "Save" }
        CheckBox { text: "Option" }
        // This button also has the "alert" variation, in addition to "mini"
        Button {
            text: "Delete"
            StyleVariation.variations: ["alert"]
        }
    }
}

Custom Controls

If your application includes custom controls that are not part of Qt Quick Controls, you can still integrate them with StyleKit. Just add a CustomControl for each of them and style them the same way you style the built-in controls:

// MyStyle.qml

Style {
    id: style
    readonly property int myControlType: 0
    CustomControl {
        controlType: style.myControlType
        background {
            implicitWidth: 120
            implicitHeight: 30
            radius: 0
        }
        hovered.background.color: "lightslategray"
        pressed.background.color: "skyblue"
    }
}

In the control's implementation, use a StyleReader with the matching controlType to read back the style properties. The correct property values are resolved by taking Themes, StyleVariations, fallback types, and property propagation into account. For a style reader to do this, it needs to know the state of your control. Therefore bind your control's state to the relevant StyleReader properties such as hovered and pressed. Each time there is a state change, any affected style properties will be updated, causing your control to repaint:

// Main.qml

component MyControl : Rectangle {
    StyleReader {
        id: styleReader
        controlType: StyleKit.style.myControlType
        hovered: hoverHandler.hovered
        pressed: tapHandler.pressed
        palette: app.palette
    }

    HoverHandler { id: hoverHandler }
    TapHandler { id: tapHandler }

    implicitWidth: styleReader.background.implicitWidth
    implicitHeight: styleReader.background.implicitHeight
    color: styleReader.background.color
    radius: styleReader.background.radius

    Text {
        font: styleReader.font
        anchors.centerIn: parent
        text: "ok"
    }
}

Custom Delegates

Each visual part of a control — background, handle, indicator, etc — is rendered by a delegate. By default StyleKit uses StyledItem for rendering, but you can replace it entirely with you own QML component.

The component needs to define two required properties that StyleKit populates automatically:

  • delegateStyle — the DelegateStyle that carries the resolved style properties (color, radius, implicit size, etc.)
  • control — the Qt Quick Control the delegate belongs to

You then assign the component to the delegate property of the visual part that you want to affect:

// import QtQuick.Templates as T

slider {
    handle.delegate: Rectangle {
        id: handle
        required property DelegateStyle delegateStyle
        required property T.Slider control
        implicitWidth: delegateStyle.implicitWidth
        implicitHeight: delegateStyle.implicitHeight
        radius: delegateStyle.radius
        color: delegateStyle.color
        Text {
            anchors.centerIn: parent
            text: handle.control.value.toFixed(0)
        }
    }
}

Overlay and Underlay

If you only want to augment the default rendering rather than replace it completely, use StyledItem as the root of your delegate. Any children you add to StyledItem are drawn on top of (overlay) the default rendering:

Style {
    component Star : Shape {
        id: star
        property color color
        ShapePath {
            fillColor: star.color
            scale: Qt.size(star.width, star.height)
            PathMove { x: 0.50; y: 0.00 }
            PathLine { x: 0.59; y: 0.35 }
            PathLine { x: 0.97; y: 0.35 }
            PathLine { x: 0.66; y: 0.57 }
            PathLine { x: 0.78; y: 0.91 }
            PathLine { x: 0.50; y: 0.70 }
            PathLine { x: 0.22; y: 0.91 }
            PathLine { x: 0.34; y: 0.57 }
            PathLine { x: 0.03; y: 0.35 }
            PathLine { x: 0.41; y: 0.35 }
            PathLine { x: 0.50; y: 0.00 }
        }
    }

    button {
        background.delegate: StyledItem {
            width: parent.width
            height: parent.height
            // Draw a star on top the default rendering
            Star {
                anchors.fill: parent
                color: "gold"
            }
        }
    }
}

To draw something underneath the default rendering instead, make your delegate an Item, place the extra content first, and embed a StyledItem as a child to render the default appearance on top:

Style {
    component Star : Shape {
        id: star
        property color color
        ShapePath {
            fillColor: star.color
            scale: Qt.size(star.width, star.height)
            PathMove { x: 0.50; y: 0.00 }
            PathLine { x: 0.59; y: 0.35 }
            PathLine { x: 0.97; y: 0.35 }
            PathLine { x: 0.66; y: 0.57 }
            PathLine { x: 0.78; y: 0.91 }
            PathLine { x: 0.50; y: 0.70 }
            PathLine { x: 0.22; y: 0.91 }
            PathLine { x: 0.34; y: 0.57 }
            PathLine { x: 0.03; y: 0.35 }
            PathLine { x: 0.41; y: 0.35 }
            PathLine { x: 0.50; y: 0.00 }
        }
    }

    slider.handle.delegate: Item {
        // Since the root item is not a StyledItem, the following
        // required properties must be defined explicitly:
        required property DelegateStyle delegateStyle
        required property QtObject control

        implicitWidth: delegateStyle.implicitWidth
        implicitHeight: delegateStyle.implicitHeight
        width: parent.width
        height: parent.height
        scale: delegateStyle.scale
        rotation: delegateStyle.rotation
        visible: delegateStyle.visible

        // Draw a star underneath the default handle delegate
        Star {
            width: parent.width * 2
            height: parent.height * 2
            anchors.centerIn: parent
            color: "gold"
        }

        StyledItem {
            // Forward delegateStyle into StyledItem. StyledItem doesn't
            // use 'control' for anything, so it can be omitted.
            delegateStyle: parent.delegateStyle
        }
    }
}

Custom Data

Custom delegates sometimes need additional styling properties that go beyond the built-in ones — for example to style child items that StyleKit knows nothing about. The data property makes this possible. It can hold any QtObject, so it can carry whatever information your delegate needs.

Like all the other style properties, data participates in style property resolution — the delegate always receives the object that matches the current control state, active theme, and style variations. Unlike the built-in properties, data is propagated as a whole — individual properties inside the object do not propagate separately:

component OverlayData : QtObject {
    property color overlayColor
}

toolButton {
    background.delegate: StyledItem {
        id: custom
        Text {
            color: custom.delegateStyle.data.overlayColor
            font.pixelSize: 30
            text: "シ"
        }
    }
    background.data: OverlayData {
        overlayColor: "sandybrown"
    }
    hovered.background.data: OverlayData {
        overlayColor: "magenta"
    }
}

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.