StyleKit Features Overview | Qt Labs StyleKit | Qt 6.11.2
Description: An introduction to the main features of StyleKit.

- Qt 6.11
- Qt Labs StyleKit
- StyleKit Features Overview
- Qt 6.11.2 Reference Documentation

# 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 undefined — such 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"
         }
     }